From: Quentin Deakin
Subject: It's time to open up the CLOSet
Date: 
Message-ID: <7rvj8j$346$1@epos.tesco.net>
Never having used CLOS in anger before, there are a couple of things that I
am struggling with. Firstly, I have defined a potato class that has a vector
dimensions attribute:

(defclass potato ()
  ((dimensions  :initarg :dimensions
                :accessor dimensions
                :type vector
                :initform #(0))))

The dimensions then correspond to #(x y z...) and any other dimensions I
there could be. It could be in 5 dimensons, a hyper-potato, say.

I am not bothered about whether this is a good thing to do but as to how I
can write a accessor methods that take a value and an integer that reads and
writes to a pototo dimension. I think the following reader is ok:

(defmethod dimension ((p potato) (i integer))
  (svref (dimensions (the potato p)) i))

But the following feels most un-CLOS like:

(defsetf dimension (p i) (value)
  `(setf (svref (dimensions (the potato ,p)) ,i) ,value))

This is not kludge'y, this is hack'y. When I tried the following:

(defsetf dimension ((p potato) (i integer)) (value)
  `(setf (svref (dimensions (the potato ,p)) ,i) ,value))

I got lots of errors. How can I fettle this? What accessors (readers,
writers) can I use to read or write a single dimension? These should take an
potato instance and an integer.

Then I would like to add all the dimensions of my potatoes together. Again I
tried:

(defmethod + ((p potato))
  ..this bit adds up all the potato dimensions..)

So that I could make instances of potatoes (maris piper, desiree, marris
javlin, King Edward etc) and the simply add them together.

But got a error in all the implementations I tried this on (CLISP and
CMUCL). Is this because my the potato definition of + was not 'congruent'
[1]? The DESCRIBEd definition of + takes the arguments (&rest number).

How can I 'overload' the + operator to do this? this is so that I can write
(+ 1potato 2potato ...) and get back #(243 443 4848 ....), say.

Any help would be most appreciated,

Best Regards,

:-) will

ps: This message is dedicated to Ethel Merman :)

[1] The definition of congruent is given by Paul Graham in 'On LISP' p. 372

From: Marc Battyani
Subject: Re: It's time to open up the CLOSet
Date: 
Message-ID: <E68EFBE7FF6F8601.A6CBFF249943B320.7002AB8642D4B6F4@lp.airnews.net>
Quentin Deakin <···········@tesco.net> wrote in message
·················@epos.tesco.net...
> Never having used CLOS in anger before, there are a couple of things that
I
> am struggling with. Firstly, I have defined a potato class that has a
vector
> dimensions attribute:
>
> (defclass potato ()
>   ((dimensions  :initarg :dimensions
>                 :accessor dimensions
>                 :type vector
>                 :initform #(0))))
>
> The dimensions then correspond to #(x y z...) and any other dimensions I
> there could be. It could be in 5 dimensons, a hyper-potato, say.
>
> I am not bothered about whether this is a good thing to do but as to how I
> can write a accessor methods that take a value and an integer that reads
and
> writes to a pototo dimension. I think the following reader is ok:
>
> (defmethod dimension ((p potato) (i integer))
>   (svref (dimensions (the potato p)) i))
>
> But the following feels most un-CLOS like:
>
> (defsetf dimension (p i) (value)
>   `(setf (svref (dimensions (the potato ,p)) ,i) ,value))

I use (defmethod (setf dimension) ...) in such a case:

CL-USER 4 > (defmethod (setf dimension) (value (p potato) (i integer))
  (setf (svref (dimensions (the potato p)) i) value))


#<standard-method (setf dimension) nil (t potato integer) 2042A51C>

CL-USER 5 > (setf a (make-instance 'potato))
#<potato 203FBE44>

CL-USER 6 > (setf (dimension a 0) 12)
12

CL-USER 7 > (dimension a 0)
12

CL-USER 8 > (describe a)
#<potato 20F07154> is a potato
dimensions      #(12)

Hope this helps

Marc Battyani
From: Tim Bradshaw
Subject: Re: It's time to open up the CLOSet
Date: 
Message-ID: <ey36718tsg8.fsf@lostwithiel.tfeb.org>
* Quentin Deakin wrote:

> But the following feels most un-CLOS like:

> (defsetf dimension (p i) (value)
>   `(setf (svref (dimensions (the potato ,p)) ,i) ,value))


(defmethod (setf dimension) (new (p potato) dim)
  (setf (svref (dimensions p) dim) new))

is more idiomatic.  I'd tend not to actually define an accessor for the
DIMENSIONS slot but hide it away, and have an INITIALIZE-INSTANCE
method which took a list of dimensions and set up the internal vector:
in which case this & other potato-friendly methods would have to use
SLOT-VALUE &c.


> (defmethod + ((p potato))
>   ..this bit adds up all the potato dimensions..)

You can't define methods for things, like +, which are not generic
functions.  You either need to define another function which *does*
have methods definable for it, or you need to do some package
trickery so that + is not CL:+ and then hack your nice new +.

In fact, you need to do some layered thing, so that your hypothetical
PLUS function (which can take any number of arguments) ends up calling
a PLUS-2ARGS function which takes just 2 args and which you can define
methods on.  I think you further need some notion of `strength' so
coercion works, if you ever want to be able to add potatos and numbers
(maybe you don't).

--tim
From: Quentin Deakin
Subject: Re: It's time to open up the CLOSet
Date: 
Message-ID: <7s2951$f97$1@barcode.tesco.net>
Tim Bradshaw wrote:

>I'd tend not to actually define an accessor for the DIMENSIONS slot but
hide it >away, and have an INITIALIZE-INSTANCE method which took a list of
dimensions >and set up the internal vector: in which case this & other
potato-friendly methods >would have to use SLOT-VALUE &c.

This seems like a good way to go, as I can sort of see the point since
dimensions is not clean (being a vector). But why would you want to not
define an accessor? Is this for ascethetic or good coding reasons?

>Tim Bradshaw wrote:
>You can't define methods for things, like +, which are not generic
>functions.

Oh.

>Tim Bradshaw wrote:
>You either need to define another function which *does*
>have methods definable for it, or you need to do some package
>trickery so that + is not CL:+ and then hack your nice new +.

Could you tell me more about the package trickery. This is for ascethic
reasons. The potato class is in the vegetable package which has plus methods
defined. I would then like to add up potatoes using (+ 1potato 2potato ...)
rather than (plus 1potato 2potato ...). :)

>In fact, you need to do some layered thing, so that your hypothetical
>PLUS function (which can take any number of arguments) ends up calling
>a PLUS-2ARGS function which takes just 2 args and which you can define
>methods on.

OK. So something like:

(defgeneric plus (&rest potatoes)
   ...passes first two potatoes to plus-2args and keeps a running total...
   ...returns running total...)

(defmethod plus-2args ((1pototato potato) (2pototato potato))
   ...acesses and adds potato dimensions together...)

Or am I barking up the wrong tree.

>I think you further need some notion of `strength' so coercion works, ...

Being hard of thinking, I don't understand this :(

>... if you ever want to be able to add potatos and numbers

This is my ultimate aim. What would be the point of a potato if you couldn't
define a method to add a floating point number to it? :)
From: Tim Bradshaw
Subject: Re: It's time to open up the CLOSet
Date: 
Message-ID: <ey34sgrter4.fsf@lostwithiel.tfeb.org>
* Quentin Deakin wrote:
> Tim Bradshaw wrote:
>> I'd tend not to actually define an accessor for the DIMENSIONS slot but
> This seems like a good way to go, as I can sort of see the point since
> dimensions is not clean (being a vector). But why would you want to not
> define an accessor? Is this for ascethetic or good coding reasons?

Well, what I would want to do is not gratuitously reveal
implementation details of the object.  If I'm going to talk about it
by (dimension x i) then I don't really see any purpose in having
another thing which says that `oh by the way, you can also get at all
the dimensions as a vector', because that commits me to either having
that vector in there for ever, or inventing one on demand. For instance if
I had lots of 1-dimensional potatoes, then I might secretly want to
economise a bit by not allocating a vector but storing the single number
directly in the slot or something.

> Could you tell me more about the package trickery. This is for ascethic
> reasons. The potato class is in the vegetable package which has plus methods
> defined. I would then like to add up potatoes using (+ 1potato 2potato ...)
> rather than (plus 1potato 2potato ...). :)

You want to do something like:

(defpackage "POTATO"
  (:use "CL")
  (:shadow "+"))

Which will cause POTATO:+ to be different than CL:+.

> (defgeneric plus (&rest potatoes)
>    ...passes first two potatoes to plus-2args and keeps a running total...
>    ...returns running total...)

There's no real point in defining PLUS as a GF (in fact, I guess you
can't do so with all optional args).  You probably want it to be an
ordinary function.  You further, if you care about performance,
probably want to have a compiler macro for it which expands the
fixed-arg-count case into explicit calls to PLUS-2-ARGS.

> This is my ultimate aim. What would be the point of a potato if you couldn't
> define a method to add a floating point number to it? :)

Quite, who needs apples and oranges when you can have potatoes and
single-floats...

--tim
From: Quentin Deakin
Subject: Re: It's time to open up the CLOSet
Date: 
Message-ID: <7s34jd$n5l$1@epos.tesco.net>
Tim Bradshaw wrote in message ...
>Well, what I would want to do is not gratuitously reveal
>implementation details of the object.
[...an excellent point about 1-D potatoes elided...]

Thank you. This is a very good reason for sorting out the access methods.
Hmm. :)

>You want to do something like:
>
>(defpackage "POTATO"
>  (:use "CL")
>  (:shadow "+"))
>
>Which will cause POTATO:+ to be different than CL:+.
>
Thank you for this. Could I now put upon you once more and ask you about
your previous posting.

You said something about 'strength' and 'coercion'. I have heard 'weak' and
'strength' wrt binding used. I thought this was something to do with garbage
collecting. Such as if something has a strong binding to something-else only
when something and something-else was dead would they be gc'd. Weak then
meaning that something and something-else could be gc'd independently.

What has this got to do with shadow'ing a function?

>> (defgeneric plus (&rest potatoes)
>>    ...passes first two potatoes to plus-2args and keeps a running
total...
>>    ...returns running total...)
>
>There's no real point in defining PLUS as a GF

Sorry. Bad code on my part. I was trying to mirror what came up when I
describe'd #'+

> You further, if you care about performance, probably want to have a
compiler macro for it which expands the fixed-arg-count case into explicit
calls to PLUS-2-ARGS.

Now 'ON Lisp' is here I can do that. But maybe not just now as my head
hurts.


Thank you for your help,

Best Regards,

:-) will

ps: >Quite, who needs apples and oranges when you can have potatoes and
>single-floats...

I presume you have read my seminal work on complex numbers fields and soft
fruit? or the use of quaternions and the broad bean? However, it all went
tragically wrong with tomato misclassification, and hence, my current
interest in cl ;)
From: Tim Bradshaw
Subject: Re: It's time to open up the CLOSet
Date: 
Message-ID: <ey31zbuu7ba.fsf@lostwithiel.tfeb.org>
* Quentin Deakin wrote:

> You said something about 'strength' and 'coercion'. I have heard 'weak' and
> 'strength' wrt binding used. I thought this was something to do with garbage
> collecting. Such as if something has a strong binding to something-else only
> when something and something-else was dead would they be gc'd. Weak then
> meaning that something and something-else could be gc'd independently.

What I meant was based on how I think the smalltalk people described
how things worked (I think this is in one of the original smalltalk
books, unfortunately I only ever borrowed them, so can't check).
Basically, I think that when you're doing arithmetic the various types
involved (integer, float &c) are described as having various strengths,
and if you do (op a b) then before doing the operation you look at the
strengths of the types involved and coerce everything to the strongest
(or you make it look like that happened anyway).  This is a really a
way of making sure the usual float-contagion rules happen right.

But I am no expert on computer arithmetic!

--tim
From: Thomas A. Russ
Subject: Re: It's time to open up the CLOSet
Date: 
Message-ID: <ymizoyhf7wr.fsf@sevak.isi.edu>
"Quentin Deakin" <···········@tesco.net> writes:
> >Tim Bradshaw wrote:
> >You either need to define another function which *does*
> >have methods definable for it, or you need to do some package
> >trickery so that + is not CL:+ and then hack your nice new +.
> 
> Could you tell me more about the package trickery. This is for ascethic
> reasons. The potato class is in the vegetable package which has plus methods
> defined. I would then like to add up potatoes using (+ 1potato 2potato ...)
> rather than (plus 1potato 2potato ...). :)

Kind of two choices, depending on how many items you want to change.
The first one is more likely to be useful.

  (defpackage "VEGGIE"
    (:use "COMMON-LISP")
    (:shadow "+" "-" "*" "/")
    )

or

  (defpackage "VEGGIE"
    (:import-from "COMMON-LISP" "DEFGENERIC" "DEFMETHOD")
    )


> 
> >In fact, you need to do some layered thing, so that your hypothetical
> >PLUS function (which can take any number of arguments) ends up calling
> >a PLUS-2ARGS function which takes just 2 args and which you can define
> >methods on.
> 
> OK. So something like:
> 
> (defgeneric plus (&rest potatoes)
>    ...passes first two potatoes to plus-2args and keeps a running total...
>    ...returns running total...)

You can't really do this with a DEFGENERIC.  DEFGENERIC is more like a
declaration than an actual defining form.  You could use DEFUN for this,
though:

(defun plus (&rest potatoes)
  (reduce #'plus-2args potatoes))

You should look at REDUCE and figure out if you need to specify any of
the keyword arguments for initial values in order to handle the cases of
calling PLUS on zero or one arguments (which is legal from the &rest
keyword).

> (defmethod plus-2args ((1pototato potato) (2pototato potato))
>    ...acesses and adds potato dimensions together...)



-- 
Thomas A. Russ,  USC/Information Sciences Institute          ···@isi.edu