From: Ari Krupnik
Subject: method combinations
Date: 
Message-ID: <86hd1f1xiq.fsf@deb.lib.aero>
I have a class that has methods specialized on it that are
expensive. I want to inherit from this class to include slots that can
hold precomputed values of these expensive methods. I want to make it
so that if I have an instance of the superclass, the expensive method
will be called, but if I have an instance of the subclass, the slot
value will be returned. I'm running into difficulties initializing the
slots. I need to invoke the expensive method, but since I have a
reference to the inheriting class, a call to to the function resolves
to a reader method. In Java, I'd say `super.c()' instead of
`c()'. What is the CLOS equivalent?

The line that I'm having trouble with is marked `tautology' below.

Ari.


(defclass dynamic ()
  (slot-a :reader a)
  (slot-b :reader b)

(defmethod c ((dy dynamic))
  ;expensive computations
  )

(defmethod d ((dy dynamic))
  ;expensive computations
  )

(defclass precomputed (dynamic)
  (slot-c :reader c)
  (slot-d :reader d))

(defmethod initialize-instance :after ((pc precomputed) &key c d)
  (with-slots (slot-c slot-d) pc
    (setf slot-c (c p))     ; tautology
    (setf slot-d (d p))))

-- 
Elections only count as free and trials as fair if you can lose money
betting on the outcome.

From: Jack Unrue
Subject: Re: method combinations
Date: 
Message-ID: <72iob2h35kb67529fkuo1bh7rqdvbkrjui@4ax.com>
On Mon, 17 Jul 2006 19:01:49 -0700, Ari Krupnik <···@lib.aero> wrote:
>
>(defclass dynamic ()
>  (slot-a :reader a)
>  (slot-b :reader b)
>
>(defmethod c ((dy dynamic))
>  ;expensive computations
>  )
>
>(defmethod d ((dy dynamic))
>  ;expensive computations
>  )
>
>(defclass precomputed (dynamic)
>  (slot-c :reader c)
>  (slot-d :reader d))
>
>(defmethod initialize-instance :after ((pc precomputed) &key c d)
>  (with-slots (slot-c slot-d) pc
>    (setf slot-c (c p))     ; tautology
>    (setf slot-d (d p))))

Ari, I would suggest getting rid of the readers for slot-c and
slot-d. In the c and d methods specialized on precomputed,
call-next-method if necessary to store the result in the
appropriate slot, then return the cached value.

-- 
Jack Unrue
From: Ari Krupnik
Subject: Re: method combinations
Date: 
Message-ID: <86d5c31ohu.fsf@deb.lib.aero>
Jack Unrue <·······@example.tld> writes:

> On Mon, 17 Jul 2006 19:01:49 -0700, Ari Krupnik <···@lib.aero> wrote:
>>
>>(defclass dynamic ()
>>  (slot-a :reader a)
>>  (slot-b :reader b)
>>
>>(defmethod c ((dy dynamic))
>>  ;expensive computations
>>  )
>>
>>(defmethod d ((dy dynamic))
>>  ;expensive computations
>>  )
>>
>>(defclass precomputed (dynamic)
>>  (slot-c :reader c)
>>  (slot-d :reader d))
>>
>>(defmethod initialize-instance :after ((pc precomputed) &key c d)
>>  (with-slots (slot-c slot-d) pc
>>    (setf slot-c (c p))     ; tautology
>>    (setf slot-d (d p))))
>
> Ari, I would suggest getting rid of the readers for slot-c and
> slot-d. In the c and d methods specialized on precomputed,
> call-next-method if necessary to store the result in the
> appropriate slot, then return the cached value.

That's what I did to get around this difficulty. The problem I saw
with this solution was that I could initialize slot-c and slot-d in
initialize-instance and not have to check slot-boundp on every
access. It's not so much that I'm worried about spending time in the
lookup (though these methods are being called from the inner loop); I
thought that taking care of the initialization in initialize-instance
would make it clearer that c and d didn't actually perform any
computation, only returned the slot values.

Ari.

-- 
Elections only count as free and trials as fair if you can lose money
betting on the outcome.
From: Tragick
Subject: Re: method combinations
Date: 
Message-ID: <1153209951.850138.276200@h48g2000cwc.googlegroups.com>
> >>(defclass dynamic ()
> >>  (slot-a :reader a)
> >>  (slot-b :reader b)
> >>
> >>(defmethod c ((dy dynamic))
> >>  ;expensive computations
> >>  )
> >>
> >>(defmethod d ((dy dynamic))
> >>  ;expensive computations
> >>  )
> >>
> >>(defclass precomputed (dynamic)
> >>  (slot-c :reader c)
> >>  (slot-d :reader d))
> >>
> >>(defmethod initialize-instance :after ((pc precomputed) &key c d)
> >>  (with-slots (slot-c slot-d) pc
> >>    (setf slot-c (c p))     ; tautology
> >>    (setf slot-d (d p))))
> >


Is precomputed really a derived class of dynamic?

(defclass precomputed ()
  ((slot-c :reader c)
   (slot-d :reader d)))

(defun expensive-computation-1 () ...)
(defun expensive-computation-2 () ...)

(defmethod initialize-instance :after ((pc precomputed) &key c d)
  (with-slots (slot-c slot-d) pc
      (setf slot-c (expensive-computation-1 ...))
      (setf slot-d (expensive-computation-2 ...)))

(defclass dynamic (precomputed)
  ((slot-a :reader a)
   (slot-b :reader b)))

(defmethod do-over ((dy dynamic))
  (with-slots (slot-a slot-b) dy
     (setf slot-a (expensive-computation-1 ...))
     (setf slot-b (expensive-computation-2 ...)))


Tragick
From: Joe Marshall
Subject: Re: method combinations
Date: 
Message-ID: <1153241843.217042.202500@h48g2000cwc.googlegroups.com>
Ari Krupnik wrote:
> I have a class that has methods specialized on it that are
> expensive. I want to inherit from this class to include slots that can
> hold precomputed values of these expensive methods. I want to make it
> so that if I have an instance of the superclass, the expensive method
> will be called, but if I have an instance of the subclass, the slot
> value will be returned. I'm running into difficulties initializing the
> slots. I need to invoke the expensive method, but since I have a
> reference to the inheriting class, a call to to the function resolves
> to a reader method. In Java, I'd say `super.c()' instead of
> `c()'. What is the CLOS equivalent?

(call-next-method)


>
> The line that I'm having trouble with is marked `tautology' below.
>
> Ari.
>
>
> (defclass dynamic ()
>   (slot-a :reader a)
>   (slot-b :reader b)
>
> (defmethod c ((dy dynamic))
>   ;expensive computations
>   )
>
> (defmethod d ((dy dynamic))
>   ;expensive computations
>   )
>
> (defclass precomputed (dynamic)
>   (slot-c :reader c)
>   (slot-d :reader d))

Here is a possible way of doing it:

(defclass precomputed (dynamic)
  (slot-c)
  (slot-d))

(defmethod c ((p precomputed))
  (unless (slot-boundp p 'slot-c)
     (setf (slot-value p 'slot-c) (call-next-method)))
  (slot-value p 'slot-c))
From: Ari Krupnik
Subject: Re: method combinations
Date: 
Message-ID: <86zmf6zlid.fsf@deb.lib.aero>
"Joe Marshall" <··········@gmail.com> writes:

> Ari Krupnik wrote:
>> to a reader method. In Java, I'd say `super.c()' instead of
>> `c()'. What is the CLOS equivalent?
>
> (call-next-method)

As far as I understand, call-next-method is only available inside
defmethod and so cannot help me here. The Javaism I mentioned is
available anywhere within a class definition.

> Here is a possible way of doing it:
>
> (defclass precomputed (dynamic)
>   (slot-c)
>   (slot-d))
>
> (defmethod c ((p precomputed))
>   (unless (slot-boundp p 'slot-c)
>      (setf (slot-value p 'slot-c) (call-next-method)))
>   (slot-value p 'slot-c))

Is there a difference between your suggestion and Jack Unrue's that I
missed?

Ari.

-- 
Elections only count as free and trials as fair if you can lose money
betting on the outcome.
From: Joe Marshall
Subject: Re: method combinations
Date: 
Message-ID: <1153260655.272429.303590@m73g2000cwd.googlegroups.com>
Ari Krupnik wrote:
>
> Is there a difference between your suggestion and Jack Unrue's that I
> missed?

Ummm... Nope.  I should have paid more attention.
From: Ari Johnson
Subject: Re: method combinations
Date: 
Message-ID: <m2lkqqcu58.fsf@hermes.theari.com>
Joe Foran <········@gmail.com> writes:

> Hi,
>
> I'm currently trying to teach myself Lisp and am working my way
> through Paul Graham's book. One of the exercises for chapter 4 asks
> the reader to define a function that takes a hash table and returns a
> corresponding assoc-list.
>
> My attempt at an answer is as follows
>
> (defun assoc-from-hash (ht)
>   (let ((assocList nil))
>     (maphash #'(lambda (k v)
>                  (append assocList (list (cons k v)))) ht )
>     assocList))
>
> I created a hash table as follows
>
> (setf ht (make-hash-table))
> (setf (gethash 'animal ht) 'dog)
> (setf (gethash 'vegetable ht) 'carrot)
> (setf (gethash 'mineral ht) 'zinc)
>
> and when I call the function
> (assoc-from-hash ht)
> Lisp evaluates this as nil
>
> What I'm trying to get is a list of the form
> ((ANIMAL . DOG) (VEGETABLE . CARROT) (MINERAL . ZINC))
> but I can't get it to work.
>
> The reason's probably something very simple but I can't see it. Can
> anyone point out where I'm going wrong?
>
> In case it's pertinent, I'm using CLISP 2.38 with SLIME on Windows XP.

Without giving it away too easily ... the problem is that you assume a
particular function is destructive while it is in fact not. (You could
say that it's "pure functional clean.")  If you used a destructive
version of the same function, it would work.

On a somewhat related note, (append assocList (list (cons k v))) feels
wasteful.  A more common idiom is to push items onto the front of the
list and then destructively reverse it afterwards.
From: Ari Johnson
Subject: Re: method combinations
Date: 
Message-ID: <m2hd1ecu30.fsf@hermes.theari.com>
Ari Johnson <·········@gmail.com> writes...in response to the wrong
topic.  Oops!
From: Thomas A. Russ
Subject: Re: method combinations
Date: 
Message-ID: <ymir70hz9ij.fsf@sevak.isi.edu>
Ari Johnson <·········@gmail.com> writes:

> On a somewhat related note, (append assocList (list (cons k v))) feels
> wasteful.  A more common idiom is to push items onto the front of the
> list and then destructively reverse it afterwards.

Actually, in this case [building the alist from a hash table], I doubt
that there is any need to reverse it afterwards.  Entries in a hash table
aren't ordered in any particular way (and any ordering wouldn't even be
stable if the hash table grows), so it really doesn't matter what order
the values end up in in the returned association list.

-- 
Thomas A. Russ,  USC/Information Sciences Institute
From: Pascal Costanza
Subject: Re: method combinations
Date: 
Message-ID: <4i3ku2F20id7U1@individual.net>
Ari Krupnik wrote:
> I have a class that has methods specialized on it that are
> expensive. I want to inherit from this class to include slots that can
> hold precomputed values of these expensive methods. I want to make it
> so that if I have an instance of the superclass, the expensive method
> will be called, but if I have an instance of the subclass, the slot
> value will be returned. I'm running into difficulties initializing the
> slots. I need to invoke the expensive method, but since I have a
> reference to the inheriting class, a call to to the function resolves
> to a reader method. 

For things like this, I typically find the unbound slots machinery quite 
useful. You can specialize slot-unbound, roughly like this:

(defmethod slot-unbound
   ((class t) (object myclass) (slot (eql 'my-slot)))
   (setf (slot-value object 'my-slot)
         (perform-expensive-computation ...)))

...and then leave the slot uninitialized. You can also force 
recomputation by calling slot-makunbound.

It doesn't seem to exactly match your requirements, but maybe you find 
this useful.


Pascal

-- 
My website: http://p-cos.net
Closer to MOP & ContextL:
http://common-lisp.net/project/closer/
From: Ari Krupnik
Subject: Re: method combinations
Date: 
Message-ID: <86vepuzjur.fsf@deb.lib.aero>
Pascal Costanza <··@p-cos.net> writes:

> For things like this, I typically find the unbound slots machinery
> quite useful. You can specialize slot-unbound, roughly like this:
>
> (defmethod slot-unbound
>   ((class t) (object myclass) (slot (eql 'my-slot)))
>   (setf (slot-value object 'my-slot)
>         (perform-expensive-computation ...)))
>
> ...and then leave the slot uninitialized. You can also force
> recomputation by calling slot-makunbound.
>
> It doesn't seem to exactly match your requirements, but maybe you find
> this useful.

Quite. Thank you for pointing me in this direction. I looked up
slot-unbound in the hyperspec, and I don't understand the meaning of
the first argument. The class of the second argument is accessible
through type-of, so it must have some other significance.

Ari.

-- 
Elections only count as free and trials as fair if you can lose money
betting on the outcome.
From: Pascal Costanza
Subject: Re: method combinations
Date: 
Message-ID: <4i56kdF23l7gU1@individual.net>
Ari Krupnik wrote:
> Pascal Costanza <··@p-cos.net> writes:
> 
>> For things like this, I typically find the unbound slots machinery
>> quite useful. You can specialize slot-unbound, roughly like this:
>>
>> (defmethod slot-unbound
>>   ((class t) (object myclass) (slot (eql 'my-slot)))
>>   (setf (slot-value object 'my-slot)
>>         (perform-expensive-computation ...)))
>>
>> ...and then leave the slot uninitialized. You can also force
>> recomputation by calling slot-makunbound.
>>
>> It doesn't seem to exactly match your requirements, but maybe you find
>> this useful.
> 
> Quite. Thank you for pointing me in this direction. I looked up
> slot-unbound in the hyperspec, and I don't understand the meaning of
> the first argument. The class of the second argument is accessible
> through type-of, so it must have some other significance.

The first argument is only relevant when you use the CLOS MOP. Until 
then you can safely ignore it, and just specialize it to 't, like in my 
example above.

(The default behavior of slot-unbound is to signal an error. You can 
change that default behavior for your own metaclass, and this is what 
you need the first argument for, to specialize it on your own metaclass. 
If this doesn't make sense to you, don't worry... ;)


Pascal

-- 
My website: http://p-cos.net
Closer to MOP & ContextL:
http://common-lisp.net/project/closer/
From: Ari Krupnik
Subject: Re: method combinations
Date: 
Message-ID: <86mzb6zdbg.fsf@deb.lib.aero>
Pascal Costanza <··@p-cos.net> writes:

>> Quite. Thank you for pointing me in this direction. I looked up
>> slot-unbound in the hyperspec, and I don't understand the meaning of
>> the first argument. The class of the second argument is accessible
>> through type-of, so it must have some other significance.
>
> The first argument is only relevant when you use the CLOS MOP. Until
> then you can safely ignore it, and just specialize it to 't, like in
> my example above.
>
> (The default behavior of slot-unbound is to signal an error. You can
> change that default behavior for your own metaclass, and this is what
> you need the first argument for, to specialize it on your own
> metaclass. If this doesn't make sense to you, don't worry... ;)

Thanks for the explanation. MOP looks like so much magic to me now,
but your post explained to me why I don't need to worry about the
argument until I learn MOP.

Ari.

-- 
Elections only count as free and trials as fair if you can lose money
betting on the outcome.