From: Tamas K Papp
Subject: lazy slots
Date: 
Message-ID: <6hich7Fln1mlU1@mid.individual.net>
I am saving information in an object, and certain slots require a time-
consuming calculation.  I can leave these slots uninitialized and call 
the function that calculates them, eg

(calculate-summary-statistics model)
(slot-value model 'average-productivity)

but it would be nicer to have this done 'on demand', eg whenever slot-
value is called, somehow checks if the slot is initialized and call 
calculate-summary-statistics if it is not.

Two questions:

1. how do you test that a slot has been assigned a value?  or should I 
just initialize them as nil, and test for that?

2. slot-value is a normal function, not a method, so I can't write 
a :before method.  Is there a way to achieve what I want?

Thanks,

Tamas

From: Zach Beane
Subject: Re: lazy slots
Date: 
Message-ID: <m33akr3os9.fsf@unnamed.xach.com>
Tamas K Papp <······@gmail.com> writes:

> I am saving information in an object, and certain slots require a time-
> consuming calculation.  I can leave these slots uninitialized and call 
> the function that calculates them, eg
>
> (calculate-summary-statistics model)
> (slot-value model 'average-productivity)
>
> but it would be nicer to have this done 'on demand', eg whenever slot-
> value is called, somehow checks if the slot is initialized and call 
> calculate-summary-statistics if it is not.
>
> Two questions:
>
> 1. how do you test that a slot has been assigned a value?  or should I 
> just initialize them as nil, and test for that?

SLOT-BOUNDP can be used to test for it. If you use NIL, the SLOT-UNBOUND
trick below doesn't work.

> 2. slot-value is a normal function, not a method, so I can't write 
> a :before method.  Is there a way to achieve what I want?

I use a method on SLOT-UNBOUND for lazy slots all the time. For a slot
named A-LAZY-SLOT in class MYCLASS, you could do this:

  (defmethod slot-unbound (class (instance myclass) 
                           (slot (eql 'a-lazy-slot)))
    (setf (a-lazy-slot instance) (lengthy-calculation instance)))

Zach
From: Tamas K Papp
Subject: Re: lazy slots
Date: 
Message-ID: <6hifkpFln1mlU2@mid.individual.net>
On Tue, 26 Aug 2008 09:10:30 -0400, Zach Beane wrote:

> I use a method on SLOT-UNBOUND for lazy slots all the time. For a slot
> named A-LAZY-SLOT in class MYCLASS, you could do this:
> 
>   (defmethod slot-unbound (class (instance myclass)
>                            (slot (eql 'a-lazy-slot)))
>     (setf (a-lazy-slot instance) (lengthy-calculation instance)))

Dear Zach,

Thank you so much.  This has simplified my code a great deal.  Again, I 
am amazed that whenever I think of something, I realize that the 
designers of CL have already thought of it and made it easy for me.  This 
is what makes CL the best programming language for me...

Best,

Tamas
From: Thomas A. Russ
Subject: Re: lazy slots
Date: 
Message-ID: <ymiy72jpx9t.fsf@blackcat.isi.edu>
Tamas K Papp <······@gmail.com> writes:

> I am saving information in an object, and certain slots require a time-
> consuming calculation.  I can leave these slots uninitialized and call 
> the function that calculates them, eg
...
> 2. slot-value is a normal function, not a method, so I can't write 
> a :before method.  Is there a way to achieve what I want?

The simplest solution is to access the slots using accessor functions
and not by using SLOT-VALUE.  That way you can easily control what
happens during slot access, since you have programmatic control of the
accesses.

You can, as Rainer Joswig mentions, use :BEFORE methods.  You can also
test slot for values with SLOT-BOUNDP.


-- 
Thomas A. Russ,  USC/Information Sciences Institute
From: Tamas K Papp
Subject: Re: lazy slots
Date: 
Message-ID: <6hklfnFmhsr0U2@mid.individual.net>
On Tue, 26 Aug 2008 09:16:14 -0700, Thomas A. Russ wrote:

> Tamas K Papp <······@gmail.com> writes:
> 
>> I am saving information in an object, and certain slots require a time-
>> consuming calculation.  I can leave these slots uninitialized and call
>> the function that calculates them, eg
> ...
>> 2. slot-value is a normal function, not a method, so I can't write a
>> :before method.  Is there a way to achieve what I want?
> 
> The simplest solution is to access the slots using accessor functions
> and not by using SLOT-VALUE.  That way you can easily control what
> happens during slot access, since you have programmatic control of the
> accesses.
> 
> You can, as Rainer Joswig mentions, use :BEFORE methods.  You can also
> test slot for values with SLOT-BOUNDP.

Thanks for all the replies.  I ended up using Zach's solution (method for 
slot-unbound), because that always works, even if slot-value is used.  
But I will keep the solution using accessors in mind when I need 
something more complex.

Anyhow, lazy slots reduced my code about 40% (when measured in lines), 
and the gains in simplicity are not easily quantifiable but are also 
amazing.

Thanks,

Tamas
From: Rainer Joswig
Subject: Re: lazy slots
Date: 
Message-ID: <joswig-214485.16314326082008@news-europe.giganews.com>
In article <··············@mid.individual.net>,
 Tamas K Papp <······@gmail.com> wrote:

> I am saving information in an object, and certain slots require a time-
> consuming calculation.  I can leave these slots uninitialized and call 
> the function that calculates them, eg
> 
> (calculate-summary-statistics model)
> (slot-value model 'average-productivity)
> 
> but it would be nicer to have this done 'on demand', eg whenever slot-
> value is called, somehow checks if the slot is initialized and call 
> calculate-summary-statistics if it is not.
> 
> Two questions:
> 
> 1. how do you test that a slot has been assigned a value?  or should I 
> just initialize them as nil, and test for that?
> 
> 2. slot-value is a normal function, not a method, so I can't write 
> a :before method.  Is there a way to achieve what I want?
> 
> Thanks,
> 
> Tamas

Before CLOS this model was very common (!) in Lisp.
Slot demons were widely used. Like IF-NEEDED,
IF-ADDED, IF-REMOVED, IF-CHANGED, and others.

SLOT-UNBOUND has been mentioned already.

You can add functionality to ACCESSOR functions.

(defclass foo ()
  ((bar :accessor foo-bar)))

Now you can write :BEFORE, :AROUND and :AFTER methods.

A :BEFORE method can check if the slot is bound,
compute a value and store it in the slot.

ACCESSORS are extensible, since they are generic functions.
SLOT-VALUE is thought as some lower level mechanism to access slots.
For example a call to SLOT-VALUE might be compiled inline.
So, when you need to extend the functionality of changing
and retrieving slot values, use accessors (readers, writers, accessors).

For some advanced usage there is also SLOT-VALUE-USING-CLASS
in the MOP. An implementation will call SLOT-VALUE-USING-CLASS
from SLOT-VALUE (usually that's something to avoid, since it
slows down the basic low-level slot access).

Here is a LispWorks version (printing the args to SLOT-VALUE-USING-CLASS):

CL-USER 49 > (defclass foo () ((bar :accessor foo-bar)) (:optimize-slot-access nil))
#<STANDARD-CLASS FOO 41300BC883>

CL-USER 50 > (defmethod clos:slot-value-using-class :before ((class clos:standard-class) (a-foo foo) (slot (eql 'bar)))
                (print (list class a-foo slot)))
#<STANDARD-METHOD CLOS:SLOT-VALUE-USING-CLASS (:BEFORE) (STANDARD-CLASS FOO (EQL BAR)) 402000FBBB>

CL-USER 51 > (let ((foo (make-instance 'foo))) (setf (slot-value foo 'bar) 10) (slot-value foo 'bar))

(#<STANDARD-CLASS FOO 41300BC883> #<FOO 40203D0943> BAR) 
10


But again, modify accessors - that's the simplest version.

But the basic machinery to add all the fancy constructs from frame systems
(like multi-valued slots, slot facets, slot demons, ...) is there.

-- 
http://lispm.dyndns.org/
From: Scott Burson
Subject: Re: lazy slots
Date: 
Message-ID: <9166529b-3d93-4acf-9082-a242416aead5@l33g2000pri.googlegroups.com>
On Aug 26, 7:31 am, Rainer Joswig <······@lisp.de> wrote:
>
> CL-USER 49 > (defclass foo () ((bar :accessor foo-bar)) (:optimize-slot-access nil))
> #<STANDARD-CLASS FOO 41300BC883>
>
> CL-USER 50 > (defmethod clos:slot-value-using-class :before ((class clos:standard-class) (a-foo foo) (slot (eql 'bar)))
>                 (print (list class a-foo slot)))

Why the `before' method on `clos:slot-value-using-class' rather than
directly on `foo-bar'?

-- Scott
From: Rainer Joswig
Subject: Re: lazy slots
Date: 
Message-ID: <joswig-9523ED.01214227082008@news-europe.giganews.com>
In article 
<····································@l33g2000pri.googlegroups.com>,
 Scott Burson <········@gmail.com> wrote:

> On Aug 26, 7:31 am, Rainer Joswig <······@lisp.de> wrote:
> >
> > CL-USER 49 > (defclass foo () ((bar :accessor foo-bar)) (:optimize-slot-access nil))
> > #<STANDARD-CLASS FOO 41300BC883>
> >
> > CL-USER 50 > (defmethod clos:slot-value-using-class :before ((class clos:standard-class) (a-foo foo) (slot (eql 'bar)))
> >                 (print (list class a-foo slot)))
> 
> Why the `before' method on `clos:slot-value-using-class' rather than
> directly on `foo-bar'?
> 
> -- Scott

As I said, usually it is better to extend the accessor function with a :before method.
But sometimes there are no accessors or you want to make sure that all operations
that read/write a slot are extended/changed.

Readers and writers are implemented via SLOT-VALUE. So the reader and writer are a higher-level
mechanism. SLOT-VALUE-USING-CLASS is the lowest-level (when the MOP is used). Extending
SLOT-VALUE-USING-CLASS will the have effect on SLOT-VALUE and the reader/writer methods.

For example you can write code using SLOT-VALUE-USING-CLASS to retrieve a slot
value from a database/object store/persistent heap/... and then SLOT-VALUE and
the reader/writer methods are all using this mechanism.

-- 
http://lispm.dyndns.org/