Page 1 of 1

Printable CLOS objects..?

Posted: Sat Jul 26, 2008 2:23 pm
by jdavis
Hi all,

I've been using Common Lisp for about a year so I'm still a relative newbie. My question is CLOS related. Up until now, I've used structures (through defstruct) heavily but I'm starting to substitute CLOS classes in for structs in certain places. I like the flexibility that CLOS gives me.

However, something that I like about structs is that they can easily be printed in a way that the reader can read them back in. This makes it easy to do simple archival of objects in plain text files. I've noticed recently that CLOS objects do not seem to be printable in the same way (they print with the funky #<> unreadable syntax). Is there any easy way to print CLOS objects in a way that they can be read back in easily?

I won't really need to use multiple inheritance in the near future so maybe structs would be good enough (using single inheritance). I know you can even define generic methods based on struct types. Should I just stick with structs? Should I start using CLOS and roll my own methods for reading and writing objects (or is there some easy way to do it)?

Regards,
Justin

Re: Printable CLOS objects..?

Posted: Sat Jul 26, 2008 8:15 pm
by findinglisp
There is nothing as generic as that for structs. You can override how CLOS objects print and the output can be essentially anything you want. The issue comes with reading things back in. Depending on your needs, you could do something as simplistic as making the printed version of a "foo" object be:

Code: Select all

(make-instance 'foo :slot1 'slot1-value :slot2 'slot2-value)
You would have to handle any inheritance yourself, whether single or multiple, and arrange to get all the slots there that you would need to recreate an object. Also, that is not the same as a reader macro, which is what structures use. Thus, using READ on the above output would give you back that whole code snippet, not a full CLOS object. You'd have to arrange to have that code executed to generate the object. You could create your own reader macro to essentially do what structures do (the #s syntax) and then call MAKE-INSTANCE under the hood.

Ultimately, CLOS is infinitely more flexible than structures, but there is a cost in complexity. If you know that structures will work for you and you can't foresee that you'll need CLOS right away, I'd go with structures. On the other hand, if you can easily envision that things are going to need to get more complex right away, then CLOS is probably a better solution as it will scale for the long term to incorporate more requirements easily.

Re: Printable CLOS objects..?

Posted: Sun Jul 27, 2008 1:31 am
by Kompottkin
findinglisp wrote:You can override how CLOS objects print and the output can be essentially anything you want.
Just in case the OP doesn't know how: You can do this by defining a method on print-object.
findinglisp wrote:... Depending on your needs, you could do something as simplistic as making the printed version of a "foo" object be:

Code: Select all

(make-instance 'foo :slot1 'slot1-value :slot2 'slot2-value)
... Thus, using READ on the above output would give you back that whole code snippet, not a full CLOS object. You'd have to arrange to have that code executed to generate the object. You could create your own reader macro to essentially do what structures do (the #s syntax) and then call MAKE-INSTANCE under the hood.
Alternately, you can also arrange for your object to be printed as something like (note the use of the sharp-dot reader macro):

Code: Select all

#.(make-instance 'foo :slot1 'slot1-value :slot2 'slot2-value)
Unfortunately, this needs *read-eval* to be true, so it's dangerous when you're reading stuff from random foreign sources. See http://www.lispworks.com/documentation/ ... 02_dhf.htm for details.

Re: Printable CLOS objects..?

Posted: Sun Jul 27, 2008 9:59 am
by jdavis
findinglisp wrote: Ultimately, CLOS is infinitely more flexible than structures
Thank you for your replies. I'm sure you're right that CLOS is more flexible but I'm not seeing where the benefits are right now (I've read ACL and PCL). Since structs are still using CLOS under the hood, are "true" classes defined with defclass really that much more flexible? It seems that with structs I can do basic single inheritance and also use generic methods and standard method combination.

The only thing that I can see wouldn't be possible would be multiple inheritance and maybe some other super fancy stuff I probably wouldn't use anyway. Is there any online article you can point me to that would explain in what situations CLOS (or really "defclass" classes) would be the right way to go?

Thanks,
Justin

Re: Printable CLOS objects..?

Posted: Sun Aug 10, 2008 1:54 pm
by WeYu
jdavis wrote: However, something that I like about structs is that they can easily be printed in a way that the reader can read them back in. This makes it easy to do simple archival of objects in plain text files. I've noticed recently that CLOS objects do not seem to be printable in the same way (they print with the funky #<> unreadable syntax). Is there any easy way to print CLOS objects in a way that they can be read back in easily?
Hi jdavis, here's a quick and dirty example of how to print objects like structs with sbcl. The code hasn't been tested much. If you want to be able to read them back in, try writing a function that turns a plist into an object and replacing print-unreadable-object with (princ "#I") or something, and then fiddling with set-dispatch-macro-character.

Code: Select all

#+sbcl 
(defun class-slots (class-designator)
  (typecase class-designator
    (symbol
     (sb-pcl:class-direct-slots (find-class class-designator)))    
    (standard-object
     (sb-pcl:class-direct-slots (class-of class-designator)))))

#+sbcl
(defun slot-name (slot)
  (sb-pcl:slot-definition-name slot))

(defmethod print-object ((me standard-object) stream)
  (let ((bound-slots-plist
           (loop for i in (class-slots me)
              for name = (slot-name i)
              when (slot-boundp me name)
              collect (intern (string name) :keyword)
              and
              collect (slot-value me name))))
    (if bound-slots-plist
        (print-unreadable-object (me stream :type t :identity t)
          (prin1 bound-slots-plist stream))
        (print-unreadable-object (me stream :type t :identity t)))))

Re: Printable CLOS objects..?

Posted: Thu Aug 14, 2008 12:35 am
by Szymon