CLOS constructors - lisp
This is a discussion on CLOS constructors - lisp ; I am a newbie to CLOS and I'm wondering if the following is possible:
I want to have an initform for a slot which is a function of the value
of another slot. For example: I create a class with ...
-
CLOS constructors
I am a newbie to CLOS and I'm wondering if the following is possible:
I want to have an initform for a slot which is a function of the value
of another slot. For example: I create a class with two slots, the
first one is initialized with a number, and the second slot should be
initialized with twice the value of the first slot, everytime I call
make-instance:
(defclass test ()
((slot0 :accessor slot0 :initform 3 :initarg :slot0)
(slot1 :accessor slot1 :initform (* 2 slot0))))
(Obviously the above doesn't work)
How is it possible to do this? How can you refer to slots within the
same class? AFAIK this is possible with structures, so is CLOS more
limited?
TIA
-
Re: CLOS constructors
On Dec 10, 1:53 pm, proton <leosara...@gmail.com> wrote:
> I am a newbie to CLOS and I'm wondering if the following is possible:
> I want to have an initform for a slot which is a function of the value
> of another slot. For example: I create a class with two slots, the
> first one is initialized with a number, and the second slot should be
> initialized with twice the value of the first slot, everytime I call
> make-instance:
>
> (defclass test ()
> ((slot0 :accessor slot0 :initform 3 :initarg :slot0)
> (slot1 :accessor slot1 :initform (* 2 slot0))))
>
> (Obviously the above doesn't work)
> How is it possible to do this? How can you refer to slots within the
> same class? AFAIK this is possible with structures, so is CLOS more
> limited?
> TIA
see defmethod initialize-instance :after
-
Re: CLOS constructors
On Dec 10, 10:57 pm, Jason <jeme...@gmail.com> wrote:
> On Dec 10, 1:53 pm, proton <leosara...@gmail.com> wrote:
>
>
>
> > I am a newbie to CLOS and I'm wondering if the following is possible:
> > I want to have an initform for a slot which is a function of the value
> > of another slot. For example: I create a class with two slots, the
> > first one is initialized with a number, and the second slot should be
> > initialized with twice the value of the first slot, everytime I call
> > make-instance:
>
> > (defclass test ()
> > ((slot0 :accessor slot0 :initform 3 :initarg :slot0)
> > (slot1 :accessor slot1 :initform (* 2 slot0))))
>
> > (Obviously the above doesn't work)
> > How is it possible to do this? How can you refer to slots within the
> > same class? AFAIK this is possible with structures, so is CLOS more
> > limited?
> > TIA
>
> see defmethod initialize-instance :after
Thanks a lot. That was quick and right on target!
-
Re: CLOS constructors
On Dec 10, 2:23 pm, proton <leosara...@gmail.com> wrote:
> On Dec 10, 10:57 pm, Jason <jeme...@gmail.com> wrote:
>
>
>
> > On Dec 10, 1:53 pm, proton <leosara...@gmail.com> wrote:
>
> > > I am a newbie to CLOS and I'm wondering if the following is possible:
> > > I want to have an initform for a slot which is a function of the value
> > > of another slot. For example: I create a class with two slots, the
> > > first one is initialized with a number, and the second slot should be
> > > initialized with twice the value of the first slot, everytime I call
> > > make-instance:
>
> > > (defclass test ()
> > > ((slot0 :accessor slot0 :initform 3 :initarg :slot0)
> > > (slot1 :accessor slot1 :initform (* 2 slot0))))
>
> > > (Obviously the above doesn't work)
> > > How is it possible to do this? How can you refer to slots within the
> > > same class? AFAIK this is possible with structures, so is CLOS more
> > > limited?
> > > TIA
>
> > see defmethod initialize-instance :after
>
> Thanks a lot. That was quick and right on target!
CLOS is really handy. It's not too big and not too small to do clever
stuff. In addition to the initialize-instance trick, there's
also a trick you can do with lazy evaluation; I do this frequently
with class definitions containing heavy calculations that you
don't want to evaluate at initialization time. Bear with me ... there
may be a few syntax glitches:
Define a class generic class:
(in-package :my-workspace)
(defclass product ()
((parent
:initarg
arent
:reader parent)
(index
:initarg :index
:reader index)))
;; I've added a few extra slots here; they're handy for establishing
product-structure.
;; If you define the slots against a special package (one where you
are unlikely have
;; function vs. generic function problems), you can avoid name-space
collisions. I
;; call mine "iv"...
(in-package :cl-user)
(defpackage "iv"
(:nicknames :IV :iv "IV")
(:use :cl-user :ccl :cl)) ;; Mac Common Lisp -- got to cover
all the bases
;; Now redefine extended-class declaring the slot-names with the
package defined for
;; this purpose and make handing initialization arguments to the slots
optional:
(in-package :my-workspace) ;; or whatever your package is
(defclass product ()
((iv:
arent
:initarg
arent
:reader iv:
arent)
(iv::index
:initarg :index
:reader iv::index))
(:default-initargs
arent nil :index nil))
;; Define a couple of lazy evaluation helper methods that retrieve the
cached
;; slot value or calculates, saves, and returns the value:
(defmethod eval-iv ((self product) (iv symbol) (rule function))
(if (slot-exists-p self iv)
(if (slot-boundp self iv)
(slot-value self iv)
(setf (slot-value self iv) (funcall rule self)))
(error "No IV slot ~a exists for product instance ~a." iv self)))
(defmethod eval-iv ((self product) (iv symbol) (rule t))
(if (slot-exists-p self iv)
(if (slot-boundp self iv)
(slot-value self iv)
(setf (slot-value self iv) rule))
(error "No IV slot ~a exists for product instance ~a." iv self)))
;; Now do something interesting with your class...
(defclass box (product)
((iv::depth :initarg :depth :reader iv::depth)
(iv::width :initarg :width :reader iv::width)
(iv::height :initarg :height :reader iv::height)
(iv::volume)
(iv::boxes))
(:default-initargs :depth 1.0 :width 1.0 :height 1.0))
;; ...and define the reader/evaluator methods for the slots that don't
have a reader specified:
(defmethod iv::volume ((self box))
(eval-iv
self
'iv::volume
#'(lambda(self)(* (iv::depth self) (iv::width self) (iv::height
self)))))
;; The rule for this slot is volume = depth x width x height
(defmethod iv::boxes ((self box))
(eval-iv
self
'iv::boxes
#'(lambda(self)
(mapcar
#'(lambda(index)(make-instance 'box
arent self
:index index
:depth (* (iv::depth self) 0.5)
:width (* (iv::width self) 0.5)
:height (* (iv::height self) 0.5)))
'(0 1 2)))))
;; The rule for this slot is: make 3 boxes with dimensions half the
size of the parent's dimensions.
;; Now try it:
(setq my-box (make-instance 'box :depth 2.0 :width 3.0 :height 4.0))
#<BOX #x9C27CA6>
;; At this point , slots iv::volume and iv::boxes are unbound.
(iv::volume my-box)
24.0
(iv::boxes my-box)
(#<BOX #x9D515CE> #<BOX #x9D528E6> #<BOX #x9D537B6>)
;; This is a stupid example and the class methods for volume and child
boxes are
;; implemented in a simplistic way for demo purposes, but you should
get the idea.
;; I have taken this step further and wrapped a macro around the whole
thing (defproduct)
;; in which I can define inputs, optional inputs, quantified child
products, descendant
;; slot values (all child products inherit the value from an
ancestor), and more
;; non-caching instance methods. I also define a macro called THIS
which implicitly
;; defines the SELF variable for the method expression so that you can
define the slot
;; method (the "rule") with a reference to the class instance itself
(which was your
;; original issue.) defmacro is your friend.
#'M
-
Re: CLOS constructors
> CLOS is really handy. It's not too big and not too small to do clever
> stuff. In addition to the initialize-instance trick, there's
> also a trick you can do with lazy evaluation; I do this frequently
> with class definitions containing heavy calculations that you
> don't want to evaluate at initialization time. Bear with me ... there
> may be a few syntax glitches:
http://common-lisp.net/project/computed-class/ does that transparently
as a MOP extension. cells, too.
- attila
-
Re: CLOS constructors
attila.lendvai@gmail.com wrote:
>> CLOS is really handy. It's not too big and not too small to do clever
>> stuff. In addition to the initialize-instance trick, there's
>> also a trick you can do with lazy evaluation; I do this frequently
>> with class definitions containing heavy calculations that you
>> don't want to evaluate at initialization time. Bear with me ... there
>> may be a few syntax glitches:
>
> http://common-lisp.net/project/computed-class/ does that transparently
> as a MOP extension. cells, too.
A pretty straightforward way is also this:
(defclass foo ()
((a :initarg :a :initform 0 :reader a)
(b :reader b)))
(defmethod slot-unbound (class (object foo) (slot (eql 'b)))
(setf (slot-value object 'b)
(1+ (slot-value object 'a))))
> (defvar *bar* (make-instance 'foo :a 5))
*BAR*
> (b *bar*)
6
The nice thing about this is that you don't have to check for slot
unboundness yourself, but can rely on the CLOS implementation to do this
for you. This is probably more efficient than having calls for
slot-boundp in your own code. You can also force recomputation by
calling slot-makunbound on the respective slots. (I have used that
technique in a few places, and it works like a charm.)
There is also a generic function slot-missing where you can do similar
things for missing slots. For example, it is very straightforward to
implement Python-style hashtable-based slots via slot-missing without
having to go through the CLOS MOP at all this way.
Pascal
--
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
-
Re: CLOS constructors
On Dec 11, 9:52 am, Pascal Costanza <p...@p-cos.net> wrote:
> attila.lend...@gmail.com wrote:
> >> CLOS is really handy. It's not too big and not too small to do clever
> >> stuff. In addition to the initialize-instance trick, there's
> >> also a trick you can do with lazy evaluation; I do this frequently
> >> with class definitions containing heavy calculations that you
> >> don't want to evaluate at initialization time. Bear with me ... there
> >> may be a few syntax glitches:
>
> >http://common-lisp.net/project/computed-class/does that transparently
> > as a MOP extension. cells, too.
>
> A pretty straightforward way is also this:
>
> (defclass foo ()
> ((a :initarg :a :initform 0 :reader a)
> (b :reader b)))
>
> (defmethod slot-unbound (class (object foo) (slot (eql 'b)))
> (setf (slot-value object 'b)
> (1+ (slot-value object 'a))))
>
> > (defvar *bar* (make-instance 'foo :a 5))
> *BAR*
>
> > (b *bar*)
> 6
>
> The nice thing about this is that you don't have to check for slot
> unboundness yourself, but can rely on the CLOS implementation to do this
> for you. This is probably more efficient than having calls for
> slot-boundp in your own code. You can also force recomputation by
> calling slot-makunbound on the respective slots. (I have used that
> technique in a few places, and it works like a charm.)
>
> There is also a generic function slot-missing where you can do similar
> things for missing slots. For example, it is very straightforward to
> implement Python-style hashtable-based slots via slot-missing without
> having to go through the CLOS MOP at all this way.
>
> Pascal
Very nice trick, thank you Pascal.
Slobodan
http://tourdelisp.blogspot.com
>
> --
> My website:http://p-cos.net
> Common Lisp Document Repository:http://cdr.eurolisp.org
> Closer to MOP & ContextL:http://common-lisp.net/project/closer/
-
Re: CLOS constructors
On Dec 11, 9:52 pm, Pascal Costanza <p...@p-cos.net> wrote:
[[ ... ]]
> >> also a trick you can do with lazy evaluation; I do this frequently
> >> with class definitions containing heavy calculations that you
[[ ... ]]
>
> A pretty straightforward way is also this:
>
> (defclass foo ()
> ((a :initarg :a :initform 0 :reader a)
> (b :reader b)))
>
> (defmethod slot-unbound (class (object foo) (slot (eql 'b)))
> (setf (slot-value object 'b)
> (1+ (slot-value object 'a))))
>
> > (defvar *bar* (make-instance 'foo :a 5))
> *BAR*
>
> > (b *bar*)
> 6
>
> The nice thing about this is that you don't have to check for slot
> unboundness yourself, but can rely on the CLOS implementation to do this
> for you. This is probably more efficient than having calls for
> slot-boundp in your own code. You can also force recomputation by
> calling slot-makunbound on the respective slots. (I have used that
> technique in a few places, and it works like a charm.)
>
> There is also a generic function slot-missing where you can do similar
> things for missing slots. For example, it is very straightforward to
> implement Python-style hashtable-based slots via slot-missing without
> having to go through the CLOS MOP at all this way.
>
Wow, this is cool. I didn't know this, but it
is so similar to the doesNotUnderstand message
for handling most "errors" in Smalltalk. I was
too slow, but was gonna suggest doing it with
a :before method. Not sure if it's better.
(defclass test ()
((a :accessor a
:initarg :a)
(b :accessor b
:initarg :b)
(lazy-product :accessor prod)))
(defmacro def-lazy-slot (accessor-name ((obj class) slot-name) &body
body)
`(defmethod ,accessor-name :before ((,obj ,class))
(when (not (slot-boundp ,obj ',slot-name))
(setf (slot-value ,obj ',slot-name)
(progn
,@body)))))
(def-lazy-slot prod ((self test) lazy-product)
(* (a self) (b self)))
which macroexpands to
(DEFMETHOD PROD :BEFORE ((SELF TEST))
(WHEN (NOT (SLOT-BOUNDP SELF 'LAZY-PRODUCT))
(SETF (SLOT-VALUE SELF 'LAZY-PRODUCT)
(PROGN (* (A SELF) (B SELF))))))
-
Re: CLOS constructors
szergling wrote:
> On Dec 11, 9:52 pm, Pascal Costanza <p...@p-cos.net> wrote:
>
> [[ ... ]]
>
>>>> also a trick you can do with lazy evaluation; I do this frequently
>>>> with class definitions containing heavy calculations that you
>
> [[ ... ]]
>
>> A pretty straightforward way is also this:
>>
>> (defclass foo ()
>> ((a :initarg :a :initform 0 :reader a)
>> (b :reader b)))
>>
>> (defmethod slot-unbound (class (object foo) (slot (eql 'b)))
>> (setf (slot-value object 'b)
>> (1+ (slot-value object 'a))))
>>
>> > (defvar *bar* (make-instance 'foo :a 5))
>> *BAR*
>>
>> > (b *bar*)
>> 6
>>
>> The nice thing about this is that you don't have to check for slot
>> unboundness yourself, but can rely on the CLOS implementation to do this
>> for you. This is probably more efficient than having calls for
>> slot-boundp in your own code. You can also force recomputation by
>> calling slot-makunbound on the respective slots. (I have used that
>> technique in a few places, and it works like a charm.)
>>
>> There is also a generic function slot-missing where you can do similar
>> things for missing slots. For example, it is very straightforward to
>> implement Python-style hashtable-based slots via slot-missing without
>> having to go through the CLOS MOP at all this way.
>>
>
> Wow, this is cool. I didn't know this, but it
> is so similar to the doesNotUnderstand message
> for handling most "errors" in Smalltalk. I was
> too slow, but was gonna suggest doing it with
> a :before method. Not sure if it's better.
>
> (defclass test ()
> ((a :accessor a
> :initarg :a)
> (b :accessor b
> :initarg :b)
> (lazy-product :accessor prod)))
>
>
> (defmacro def-lazy-slot (accessor-name ((obj class) slot-name) &body
> body)
> `(defmethod ,accessor-name :before ((,obj ,class))
> (when (not (slot-boundp ,obj ',slot-name))
> (setf (slot-value ,obj ',slot-name)
> (progn
> ,@body)))))
>
> (def-lazy-slot prod ((self test) lazy-product)
> (* (a self) (b self)))
>
> which macroexpands to
>
> (DEFMETHOD PROD :BEFORE ((SELF TEST))
> (WHEN (NOT (SLOT-BOUNDP SELF 'LAZY-PRODUCT))
> (SETF (SLOT-VALUE SELF 'LAZY-PRODUCT)
> (PROGN (* (A SELF) (B SELF))))))
Well, there are two things to note:
- Whenever you call slot-value or a reader on a slot, CLOS has to do the
unboundness check anyway. So idioms like (and (slot-boundp object
some-slot) (slot-value object some-slot)) actually result in two checks
for unboundness, which is not so efficient.
- Reader methods are especially optimized to yield fast slot accesses. I
don't know what impact before/after/around on slot accessors actually
have in terms of performance, but I'd be careful here.
- Methods on slot-unbound avoid all these potential performance issues
from the start.
There is also a potential problem wrt to multi-threading: In between
your own slot-boundp check and the eventual call to slot-value, another
thread might perform a slot-makunbound on the slot in question. In
general, it is better not to perform such checks explicitly, but rather
delegate this to an implicit check where you can react to exceptional
situations where your assumptions don't hold. (Methods on slot-unbound
are an example for this.) This should help you to better avoid race
conditions.
This is only a hypothetical remark, though, since CLOS and
multithreading don't go that well together, and you have to be more
careful than that anyway...
Pascal
--
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
-
Re: CLOS constructors
On Dec 11, 10:27 am, szergling <senatorZergl...@gmail.com> wrote:
> On Dec 11, 9:52 pm, Pascal Costanza <p...@p-cos.net> wrote:
> > There is also a generic function slot-missing where you can do similar
> > things for missing slots. For example, it is very straightforward to
> > implement Python-style hashtable-based slots via slot-missing without
> > having to go through the CLOS MOP at all this way.
>
> Wow, this is cool. I didn't know this, but it
> is so similar to the doesNotUnderstand message
> for handling most "errors" in Smalltalk.
Yes, slot-missing and slot-unbound are kind of like doesNotUnderstand
for slot access. no-applicable-method is even more like
doesNotUnderstand in that it gives you a hook into the machinery where
no method was found; however, due partly to the way CLOS generic
functions work, and partly to its design, it's not an especially
useful hook.
Similar Threads
-
By Application Development in forum lisp
Replies: 53
Last Post: 12-30-2007, 12:18 PM
-
By Application Development in forum lisp
Replies: 4
Last Post: 10-04-2007, 05:30 PM
-
By Application Development in forum c++
Replies: 3
Last Post: 08-04-2007, 01:34 PM
-
By Application Development in forum lisp
Replies: 14
Last Post: 08-01-2007, 03:41 AM
-
By Application Development in forum c++
Replies: 5
Last Post: 06-07-2007, 06:09 AM