From: Chris Capel
Subject: Slot accessors and syntax
Date: 
Message-ID: <10rthgnii1tcp1d@corp.supernews.com>
Coming from C#, the most painful aspect of Lisp seems to me to be using
objects. Yes, the system is more powerful. But the syntax just isn't made
for it. When I have an object foo-inst of class foo, with slots bar and
baz, here are my options for accessing the slots:

1) (slot-value foo-inst 'bar), (slot-value foo-inst 'baz)
2) (with-slots (bar baz) foo-inst ...)
   Declaring accessors in my defclass,
3)   (foo.bar foo-inst), (foo.baz foo-inst)
   and
4)   (with-accessors (foo.bar foo.baz) foo-inst ...)

I've heard it recommended that one use accessors preferably to slots, so
that if you ever want to change the slots of a class, you can leave the old
accessors (with the proper translations) and change the slots, and nothing
breaks. I tend to agree with that. Similarly, Microsoft recommends not
making public fields (slots) in your class, but only properties (accessors)
wrapping the fields.

But the trouble with declaring accessors in CLOS is that they become
globally visible functions in the package. I don't think that's a good
thing. When you give a slot a symbol for a name, that symbol only signifies
that slot within a (slot-value ...) call or something similar. It can be
reused for any number of things without technical problems. But when you
use an accessor, it takes up the function slot for that symbol (though
since it's a generic function you can use it for other generic functions,
including other accessors). So it's common practice to either include the
class name in the symbols of most accessors, or to make their names
otherwise different from their slots so that they're unambiguous. So you
end up with redundancies like

(bullet.position bullet) ;;Well, of /course/ bullet is an instance of
                         ;;bullet. I didn't need the code to shout it
                         ;;at me /twice/. 

or weirdness where the slot name is one thing and the accessor a slightly
different thing.

On the other hand, in most cases I can just give all my accessors names
identical to my slots--except in the case where my slots have names
identical to exported non-generic function symbols from another package,
like common-lisp or a library. Almost makes me want to switch to a Lisp-3.

If I really wanted, I could wrap defclass to give accessors gensym names and
make a generic function that keeps up with the relation of slot names to
accessor gensyms. But ... that seems a bit excessive... . Hmm... let me
see...

(defgeneric accessor-value (instance accessor))
(defgeneric (setf accessor-value) (val instance accessor))

(defmacro my-defclass (name superclasses slots &rest options)
  (let ((gensyms (make-hash-table))
        (hash-name (read-from-string (concatenate 'string (symbol-name name)
                                                  "-accessor-hash"))))
    (lambda-mapcar (slot slots)
      (setf (gethash (if (atom slot) slot (car slot))
                     gensyms)
            (gensym)))
    `(progn
       (defparameter ,hash-name ,gensyms)
       (defmethod accessor-value ((instance ,name) accessor)
         (funcall (symbol-function (gethash accessor ,hash-name)) instance))
       (defmethod (setf accessor-value) (val (instance ,name) accessor)
         ;;broken
         (funcall `(function ,(list 'setf (gethash accessor ,hash-name)))
                   val instance))
       (defclass ,name ,superclasses
         ,(lambda-mapcar (slot slots)
            (if (atom slot)
                (list slot :accessor (gethash slot gensyms))
                (if (pos (position :accessor slot))
                    (error ":ACCESSOR keyword not allowed in this defclass. Forget it.")
                    (append slot (list :accessor (gethash (car slot) gensyms))))))
         ,@options))))

Incidentally, the setf method doesn't work in this. It appears that if the
setf places for slot accessors in SBCL are implemented as expanders, not
functions. ANSI says in 5.1.1.2 that you can't depend on there being
functions. So I'm left out in the cold on this one. I don't think there's a
way to avoid a runtime eval in this situation. But I don't feel like
working on this one any more. Maybe one of you smart folks can tell me how
this can all be done with MOP more cleanly.

Moving on, the with-accessors and with-slots macros are even worse, IMO.
Now, I'd still use them occasionally in sections of code where I wanted a
really short abbreviation for an accessor, but as the primary way of
getting to class members, they're worse than (1) and (3). 

So, what to do? Here's what I've tried.

(set-macro-character
 #\$
 #'(lambda (stream char)
     (declare (ignore char))
     (let ((*readtable* (copy-readtable)))
       (set-syntax-from-char #\. #\ )
       (let ((instance (read stream t nil t))
             (accessor (read stream t nil t)))
         ;; this form can be `(accessor-value ,instance ',accessor)
         ;; if you use the above macro
         `(,accessor ,instance)))))

Now I can type $foo-inst.bar and $foo-inst.baz (assuming bar and baz are
bound to the appropriate accessors) as much as I want, as well as
"$foo-inst bar" and "$ foo-inst bar" and "$(some-func $foo-inst.bar).baz".
I get my subjects and verbs back forwards the way I like them. And I type
two fewer parentheses for every accessor call. Nice. But that $ reminds me
too much of PHP. So could I get rid of it somehow?

I think I'm willing to forego the "." as used for constructing dotted lists
(not exactly something I do all the time), and even deal with the annoyance
of using libraries that have dots in their exported symbols, if I were able
to give it some sort of reader magic that lets me type, e.g.,
"foo-inst.baz" and have it read in as "(baz foo-inst)". Or maybe a
different character would be better. (Suggestions?) But I don't think it's
actually /possible/ to write such a magic macro. Can a read macro reclaim
the previous symbol in the stream and replace it with something else?
Something similar seems to be going on with the #'(lambda () ...) ==
(lambda () ...) equivalence, but I'm not sure.

Has anyone taken a difference approach to this, perhaps?

Chris Capel

P.S. Utility functions for the above my-defclass macro:

(defun group (n args)
  (let ((start (cons nil nil)))
    (do ((cur start (cdr cur))
         (args args (nthcdr n args)))
        ((null args) (cdr start))
      (setf (cdr cur)
            (cons (subseq args 0 n)
                  nil)))))

(defmacro lambda-mapcar (args &body body)
  "Call with (lambda-mapcar (arg1 list1 arg2 list2)
             lambda-body)"
  (let ((grouped-args (group 2 args)))
    `(mapcar (lambda ,(mapcar #'first grouped-args) ,@body)
             ,@(mapcar #'second grouped-args))))

From: Kenny Tilton
Subject: Re: Slot accessors and syntax
Date: 
Message-ID: <4vCvd.24668$Yh2.11122723@twister.nyc.rr.com>
Chris Capel wrote:

> Coming from C#, the most painful aspect of Lisp seems to me to be using
> objects. Yes, the system is more powerful. But the syntax just isn't made
> for it. When I have an object foo-inst of class foo, with slots bar and
> baz, here are my options for accessing the slots:
> 
> 1) (slot-value foo-inst 'bar), (slot-value foo-inst 'baz)
> 2) (with-slots (bar baz) foo-inst ...)
>    Declaring accessors in my defclass,
> 3)   (foo.bar foo-inst), (foo.baz foo-inst)
>    and
> 4)   (with-accessors (foo.bar foo.baz) foo-inst ...)

5) Declaring accessors bar and baz
> 
> I've heard it recommended that one use accessors preferably to slots, so
> that if you ever want to change the slots of a class, you can leave the old
> accessors (with the proper translations) and change the slots, and nothing
> breaks. I tend to agree with that. Similarly, Microsoft recommends not
> making public fields (slots) in your class, but only properties (accessors)
> wrapping the fields.
> 
> But the trouble with declaring accessors in CLOS is that they become
> globally visible functions in the package.

This has not caused me any trouble in quite a bit of CLOSsing over the 
past eight years.

> I don't think that's a good
> thing. When you give a slot a symbol for a name, that symbol only signifies
> that slot within a (slot-value ...) call or something similar. It can be
> reused for any number of things without technical problems. But when you
> use an accessor, it takes up the function slot for that symbol (though
> since it's a generic function you can use it for other generic functions,
> including other accessors). So it's common practice to either include the
> class name in the symbols of most accessors, or to make their names
> otherwise different from their slots so that they're unambiguous.

It is not my practice, but then I am uncommon.

> So you
> end up with redundancies like
> 
> (bullet.position bullet) ;;Well, of /course/ bullet is an instance of
>                          ;;bullet. I didn't need the code to shout it
>                          ;;at me /twice/. 
> 
> or weirdness where the slot name is one thing and the accessor a slightly
> different thing.
> 
> On the other hand, in most cases I can just give all my accessors names
> identical to my slots--except in the case where my slots have names
> identical to exported non-generic function symbols from another package,
> like common-lisp or a library.

I think that happened to me three times over the years. Stuff like 
"window" and "color". I lived. :)

kenny

-- 
Cells? Cello? Celtik?: http://www.common-lisp.net/project/cells/
Why Lisp? http://alu.cliki.net/RtL%20Highlight%20Film
From: Marco Baringer
Subject: Re: Slot accessors and syntax
Date: 
Message-ID: <m2oegxdlo4.fsf@bese.it>
Chris Capel <······@iba.nktech.net> writes:

> I think I'm willing to forego the "." as used for constructing dotted lists
> (not exactly something I do all the time), and even deal with the annoyance
> of using libraries that have dots in their exported symbols, if I were able
> to give it some sort of reader magic that lets me type, e.g.,
> "foo-inst.baz" and have it read in as "(baz foo-inst)". Or maybe a
> different character would be better. (Suggestions?) But I don't think it's
> actually /possible/ to write such a magic macro. Can a read macro reclaim
> the previous symbol in the stream and replace it with something else?
> Something similar seems to be going on with the #'(lambda () ...) ==
> (lambda () ...) equivalence, but I'm not sure.

you've got that backwards, (lambda ...) is a macro which expands into
(function (lambda ...)). function is a special op.

anyway, you can't write a read macro which suffixs the interesting
symbol (not without parsing the lisp code yourself). what you can do
is write a macro-character, like the one you have, use something other
than #\.. and extend it to multiple accessors: (using #\^)

#^instance^acc1^acc2^acc3

i don't know if writing this in java order or lisp order is better,
you'll see. 

p.s. - what you really want is to get unicode sbcl and use some arrow
shaped char.

-- 
-Marco
Ring the bells that still can ring.
Forget your perfect offering.
There is a crack in everything.
That's how the light gets in.
     -Leonard Cohen
From: Chris Capel
Subject: Re: Slot accessors and syntax
Date: 
Message-ID: <10rv22nomjj1me7@corp.supernews.com>
Marco Baringer wrote:

> Chris Capel <······@iba.nktech.net> writes:
> 
>> it's actually /possible/ to write such a magic macro. Can a read macro
>> reclaim the previous symbol in the stream and replace it with something
>> else? Something similar seems to be going on with the #'(lambda () ...)
>> == (lambda () ...) equivalence, but I'm not sure.
> 
> you've got that backwards, (lambda ...) is a macro which expands into
> (function (lambda ...)). function is a special op.

Oh, so "function" keeps the inner lambda from being macroexpanded again?
That makes sense.

Chris Capel
From: Peter Seibel
Subject: Re: Slot accessors and syntax
Date: 
Message-ID: <m3pt1cjrzn.fsf@javamonkey.com>
Chris Capel <······@iba.nktech.net> writes:

> Marco Baringer wrote:
>
>> Chris Capel <······@iba.nktech.net> writes:
>> 
>>> it's actually /possible/ to write such a magic macro. Can a read macro
>>> reclaim the previous symbol in the stream and replace it with something
>>> else? Something similar seems to be going on with the #'(lambda () ...)
>>> == (lambda () ...) equivalence, but I'm not sure.
>> 
>> you've got that backwards, (lambda ...) is a macro which expands into
>> (function (lambda ...)). function is a special op.
>
> Oh, so "function" keeps the inner lambda from being macroexpanded again?
> That makes sense.

Yup. Just the way it keeps a symbol from being evaluated:

  (function car) ;; a.k.a. #'car

-Peter

-- 
Peter Seibel                                      ·····@javamonkey.com

         Lisp is the red pill. -- John Fraser, comp.lang.lisp
From: Björn Lindberg
Subject: Re: Slot accessors and syntax
Date: 
Message-ID: <hcsekhtgdkg.fsf@my.nada.kth.se>
Chris Capel <······@iba.nktech.net> writes:

> Coming from C#, the most painful aspect of Lisp seems to me to be using
> objects. Yes, the system is more powerful. But the syntax just isn't made
> for it. When I have an object foo-inst of class foo, with slots bar and
> baz, here are my options for accessing the slots:
> 
> 1) (slot-value foo-inst 'bar), (slot-value foo-inst 'baz)
> 2) (with-slots (bar baz) foo-inst ...)
>    Declaring accessors in my defclass,
> 3)   (foo.bar foo-inst), (foo.baz foo-inst)
>    and
> 4)   (with-accessors (foo.bar foo.baz) foo-inst ...)
> 
> I've heard it recommended that one use accessors preferably to slots, so
> that if you ever want to change the slots of a class, you can leave the old
> accessors (with the proper translations) and change the slots, and nothing
> breaks. I tend to agree with that. Similarly, Microsoft recommends not
> making public fields (slots) in your class, but only properties (accessors)
> wrapping the fields.
> 
> But the trouble with declaring accessors in CLOS is that they become
> globally visible functions in the package. I don't think that's a good
> thing. When you give a slot a symbol for a name, that symbol only signifies
> that slot within a (slot-value ...) call or something similar. It can be
> reused for any number of things without technical problems. But when you
> use an accessor, it takes up the function slot for that symbol (though
> since it's a generic function you can use it for other generic functions,
> including other accessors). So it's common practice to either include the
> class name in the symbols of most accessors, or to make their names
> otherwise different from their slots so that they're unambiguous. So you
> end up with redundancies like
> 
> (bullet.position bullet) ;;Well, of /course/ bullet is an instance of
>                          ;;bullet. I didn't need the code to shout it
>                          ;;at me /twice/. 
> 
> or weirdness where the slot name is one thing and the accessor a slightly
> different thing.
> 
> On the other hand, in most cases I can just give all my accessors names
> identical to my slots--except in the case where my slots have names
> identical to exported non-generic function symbols from another package,
> like common-lisp or a library. Almost makes me want to switch to a Lisp-3.

The problem you describe is a namespace issue, so you need a namespace
tool to solve it. That tool is packages. In CL, classes are not used
for creating namespaces. So you do

  (defpackage foo
    (:use cl)
    (:shadow position))

  (position bullet)
  ; [...]
  (cl:position #\o "foo")

Personally, I consider this to be a much better way of going about it
than what is traditional in certain other OO languages. Now you have
complete, fine-grained control over your namespaces. You can still put
each class in its own namespace (package) if you want -- or half of
it, or two classes in one namespace.

Generic functions permits you to use methods in higher-order settings,
for example as arguments to mapcar, remove-if-not and friends, which
is very convenient. This is not possible by making method syntax
distinct from regular function syntax, as is done in those other
languages.

My suggestion is that you give the CL way some time before you reject
it, or try to improve on it. It takes a bit of a mental switch to use
CLOS optimally when you are only used to OO programming in other
languages.


Bj�rn
From: Tim Bradshaw
Subject: Re: Slot accessors and syntax
Date: 
Message-ID: <ey3pt1cx3wu.fsf@cley.com>
* Bj�rn Lindberg wrote:

> The problem you describe is a namespace issue, so you need a namespace
> tool to solve it. That tool is packages. In CL, classes are not used
> for creating namespaces. So you do

>   (defpackage foo
>     (:use cl)
>     (:shadow position))

>   (position bullet)
>   ; [...]
>   (cl:position #\o "foo")

> Personally, I consider this to be a much better way of going about it
> than what is traditional in certain other OO languages. Now you have
> complete, fine-grained control over your namespaces. You can still put
> each class in its own namespace (package) if you want -- or half of
> it, or two classes in one namespace.

Right.  And you can then do lots of namespace tricks with packages:
name them in a structured way, use conduits to let packages extend
other packages and so on.

--tim
From: Tim Bradshaw
Subject: Re: Slot accessors and syntax
Date: 
Message-ID: <ey3u0qox42f.fsf@cley.com>
* Chris Capel wrote:
> Coming from C#, the most painful aspect of Lisp seems to me to be using
> objects. Yes, the system is more powerful. But the syntax just isn't made
> for it. When I have an object foo-inst of class foo, with slots bar and
> baz, here are my options for accessing the slots:

> 1) (slot-value foo-inst 'bar), (slot-value foo-inst 'baz)
> 2) (with-slots (bar baz) foo-inst ...)
>    Declaring accessors in my defclass,
> 3)   (foo.bar foo-inst), (foo.baz foo-inst)
>    and
> 4)   (with-accessors (foo.bar foo.baz) foo-inst ...)

Why on earth are you obsessing away about slots?  Obsess about
protocols instead: your classes partake in some protocol which is
typically specified in terms of a bunch of generic functions taking
instances of one or more classes as arguments.  Some of those generic
functions might end up referring to slots, but if you're documenting
slots as part of the protocol you're doing something badly wrong.

> But the trouble with declaring accessors in CLOS is that they become
> globally visible functions in the package. I don't think that's a good
> thing.

Of course it is.  Packages contain (one or more) protocols, and its
entirely appropriate for the function, macro and class names which
define the protocol to be exported from the package (unless it is some
kind of sub protocol when it may not be exported).

> So it's common practice to either include the
> class name in the symbols of most accessors, or to make their names
> otherwise different from their slots so that they're unambiguous. So you
> end up with redundancies like
>
> (bullet.position bullet) ;;Well, of /course/ bullet is an instance of
>                          ;;bullet. I didn't need the code to shout it
>                          ;;at me /twice/. 


Choosing decent names is an art, yes, especially as CL is defined in
terms of things with names (like POSITION) rather than squiggles.
But, well, why not LOCATION say:

LOCATION (COM.CLEY.PHYSICS.NEWTONIAN): return the location of an
   object in 3-space.  Default coordinate system ...  
(SETF LOCATION) (COM.CLEY.PHYSICS.NEWTONIAN): set the location ..

> or weirdness where the slot name is one thing and the accessor a slightly
> different thing.

Again: slot names are an *implementation detail*.

> Moving on, the with-accessors and with-slots macros are even worse, IMO.
> Now, I'd still use them occasionally in sections of code where I wanted a
> really short abbreviation for an accessor, but as the primary way of
> getting to class members, they're worse than (1) and (3). 

Again, these are implementation details: client code should never know
if something in a protocol is an accessor, a slot, or what.


All of this sounds like you're trying to make CL be the same as Java
or something  (although I don't think exporting slot names in Java is
really considered good style). Don't do that!

--tim
From: Chris Capel
Subject: Re: Slot accessors and syntax
Date: 
Message-ID: <10rv37r3f100b3@corp.supernews.com>
Tim Bradshaw wrote:

> * Chris Capel wrote:
>> Coming from C#, the most painful aspect of Lisp seems to me to be using
>> objects. Yes, the system is more powerful. But the syntax just isn't made
>> for it. When I have an object foo-inst of class foo, with slots bar and
>> baz, here are my options for accessing the slots:
> 
>> 1) (slot-value foo-inst 'bar), (slot-value foo-inst 'baz)
>> 2) (with-slots (bar baz) foo-inst ...)
>>    Declaring accessors in my defclass,
>> 3)   (foo.bar foo-inst), (foo.baz foo-inst)
>>    and
>> 4)   (with-accessors (foo.bar foo.baz) foo-inst ...)
> 
> Why on earth are you obsessing away about slots?  Obsess about
> protocols instead: your classes partake in some protocol which is
> typically specified in terms of a bunch of generic functions taking
> instances of one or more classes as arguments.  Some of those generic
> functions might end up referring to slots, but if you're documenting
> slots as part of the protocol you're doing something badly wrong.
>
>> But the trouble with declaring accessors in CLOS is that they become
>> globally visible functions in the package. I don't think that's a good
>> thing.
> 
> Of course it is.  Packages contain (one or more) protocols, and its
> entirely appropriate for the function, macro and class names which
> define the protocol to be exported from the package (unless it is some
> kind of sub protocol when it may not be exported).

With the "protocol" view, which I don't really understand, I can see how you
could argue this. But I don't understand well enough to actually use it, so
let's do something.

Let's talk about the utility of using classes during the evolution of a
program. When I start thinking about what I want my new program to do, I
often find that I want "objects"--I can cleanly describe the desired logic
of the program using different classes of objects and actions performed on
them. Now, there are really three different elements to the objects I'm
talking about. There's the categorization itself--the division of the
problem into interacting objects. There are the actions that are performed
on the objects. And there's the internal state of the objects. Now, this
third element has to be represented somehow as data that gets associated
somehow with instances of each class of object. A slot.

In the real world, there's no such thing as an "object", as they're used in
languages. There's no strict division between one object and another, one
class or another. Thus, there are no "objects" to really have complicated
state, only layer upon layer of actions whose ongoing existence constitutes
what, on a higher level, we call the internal state of the object. These
actions, when represented in code, are functions--generic or not. The
emergent properties of systems are amenable to simplification with
acceptable loss of information. One example of this simplification is jpeg
compression. Another is object slots. One consequence of this fact is that
one often finds that the slots one has chosen are inappropriate for the
problem, as simplifications are bound to be simplistic, and one changes
them around. One never changes the actions performed on the objects
(assuming they're coded correctly), unless the requirements change
("changing requirements" includes incompatible interface changes to other
objects), because the actions are the real-world analogue that the object
abstraction is built on. One might append to the actions, but the
functionality of the system is what you're modeling, so if you lose
actions, you lose part of the system. If you lose the data underneath,
different actions might make up for the difference in the substrate and
accomplish the same thing in the end.

Now, when I create an object, my immediate use for it is to store
information describing the state of these simplifications or abstractions
about my problem. It's a grab-bag of slots. It's ugly, yes, and primitive,
yes, but that's what an abstraction /is/. Then I start to define actions
acting on the slots. Then I define actions connecting other actions to
global state, and write a driver function for it all that sets off the
system. You can't get rid of the slots in this picture, no matter how many
actions you add. All you can do is bury them, or replace them with actions
that refer to lower-level slots. I don't see how burying them more deeply
is always better. Are you saying this? I'm not sure. Making the slots
lower-level, and more general, is good when the program isn't flexible
enough yet, when it doesn't present an accurate enough model for the
problem it's solving, but beyond that point it's pointless.

What /is/ it that were you saying about protocols, in view of all this? I've
presented my understanding of object-oriented abstraction. How is it
different from what you've presented?

Chris Capel
From: Geoffrey Summerhayes
Subject: Re: Slot accessors and syntax
Date: 
Message-ID: <dUSvd.11413$%p1.1085336@news20.bellglobal.com>
"Chris Capel" <······@iba.nktech.net> wrote in message ···················@corp.supernews.com...

<* snipped *>

> Now, when I create an object, my immediate use for it is to store
> information describing the state of these simplifications or abstractions
> about my problem. It's a grab-bag of slots. It's ugly, yes, and primitive,
> yes, but that's what an abstraction /is/. Then I start to define actions
> acting on the slots. Then I define actions connecting other actions to
> global state, and write a driver function for it all that sets off the
> system. You can't get rid of the slots in this picture, no matter how many
> actions you add. All you can do is bury them, or replace them with actions
> that refer to lower-level slots. I don't see how burying them more deeply
> is always better. Are you saying this? I'm not sure. Making the slots
> lower-level, and more general, is good when the program isn't flexible
> enough yet, when it doesn't present an accurate enough model for the
> problem it's solving, but beyond that point it's pointless.


Well, one of the ideas behind an object is to have it maintain state
so you can't get away from having slots. As for access to slots,
accessing them directly causes problems if the class is redesigned
eliminating or replacing those slots. C++ has a problem if you
design public slots to allow things like:

a=new foo;
a.y=4;

Eliminate or replace y in class foo, how much code needs to be changed?
The trick is to make them 'private' and write accessors. As an advantage
the accessors can check to make sure the object remains in a valid state.
I don't know about C#, in C++ you'd use something like a.set_y(4);,
Borland's Delphi has a neat little trick involving readers and writers
so you can write a.y := 4; and the compiler automatically changes it
to a.set_y(4);

The next problem is naming accessors, a.y works because 'a' clarifies
which 'y'. In Lisp it doesn't work that way. (<name> :accessor <name>)
has a problem when name is a symbol like 'list'. So we generally use
(<name> :accessor <class>-<name>)

You could always do (<name> :accessor csv-<name>) as long as you
rule that csv- is for generic methods with an accessor-style lambda
list. (that was a mouthful)

> What /is/ it that were you saying about protocols, in view of all this?
> I've
> presented my understanding of object-oriented abstraction. How is it
> different from what you've presented?
>

Exaaaample time!

(defun run-test()
  (mapcar (lambda (fn)
            (let ((a (create-foo 3 5))
                  (b (create-bar 3 5)))
              (list (cons (funcall fn 20 a)
                          (funcall fn 20 b))
                    (cons (csv-x a) (csv-x b))
                    (cons (csv-y a) (csv-y b))
                    (cons (csv-sum a) (csv-sum b)))))
          (list #'(setf csv-x) #'(setf csv-y)
                #'(setf csv-sum))))
RUN-TEST
lisp > (run-test)
(((20 . 20) (20 . 20) (5 . 5) (25 . 25))
 ((20 . 20) (3 . 3) (20 . 20) (23 . 23))
 ((20 . 20) (3 . 3) (17 . 17) (20 . 20)))

They look like three accessors, the two objects behave
the same, but look at the definitions

(defclass foo()
  ((x :accessor csv-x :initarg :x)
   (sum :accessor csv-sum :initarg :sum)))

(defclass bar()
  ((x :accessor csv-x :initarg :x)
   (y :accessor csv-y :initarg :y)))

(defun create-foo (x y)
  (make-instance 'foo :x x :sum (+ x y)))

(defun create-bar (x y)
  (make-instance 'bar :x x :y y))

(defmethod csv-y ((foo foo))
  (- (csv-sum foo) (csv-x foo)))

(defmethod (setf csv-y) (new (foo foo))
  (setf (csv-sum foo) (+ (csv-x foo) new))
  new)

(defmethod (setf csv-x) :around (new (foo foo))
  ;; since foo maintains the sum in a slot
  ;; changing x requires us to recalculate
  ;; the sum as well
  (let ((old (csv-x foo)))
    (call-next-method)
    (incf (csv-sum foo) (- new old)))
  new)

(defmethod csv-sum ((bar bar))
  (+ (csv-x bar) (csv-y bar)))

(defmethod (setf csv-sum) (new (bar bar))
  (setf (csv-y bar) (- new (csv-x bar)))
  new)

CREATE-FOO,CREATE-BAR,CSV-X,CSV-Y,CSV-SUM form the
protocol for dealing with foo and bar instances,
you don't need to know the slot names or even how
many there are.

--
Geoff
From: Zach Beane
Subject: Re: Slot accessors and syntax
Date: 
Message-ID: <m3is737mfh.fsf@unnamed.xach.com>
"Geoffrey Summerhayes" <·······@NhOoStPmAaMil.com> writes:

> The next problem is naming accessors, a.y works because 'a' clarifies
> which 'y'. In Lisp it doesn't work that way. (<name> :accessor <name>)
> has a problem when name is a symbol like 'list'. So we generally use
> (<name> :accessor <class>-<name>)

Speak for yourself! I think <class>-<name> is a really bad idea in the
presence of inheritance and generic functions. Someone else in this
thread mentioned shadowing, and I think that's a preferable way to
manage conflicts with other CL symbols. Someone else (perhaps the same
person) mentioned that both a house and a bullet may have a location;
why should the GF be tagged with any particular class?

Zach
From: Geoffrey Summerhayes
Subject: Re: Slot accessors and syntax
Date: 
Message-ID: <ZN%vd.11048$pb.819109@news20.bellglobal.com>
"Zach Beane" <····@xach.com> wrote in message 
···················@unnamed.xach.com...
> "Geoffrey Summerhayes" <·······@NhOoStPmAaMil.com> writes:
>
>> The next problem is naming accessors, a.y works because 'a' clarifies
>> which 'y'. In Lisp it doesn't work that way. (<name> :accessor <name>)
>> has a problem when name is a symbol like 'list'. So we generally use
>> (<name> :accessor <class>-<name>)
>
> Speak for yourself! I think <class>-<name> is a really bad idea in the
> presence of inheritance and generic functions. Someone else in this
> thread mentioned shadowing, and I think that's a preferable way to
> manage conflicts with other CL symbols. Someone else (perhaps the same
> person) mentioned that both a house and a bullet may have a location;
> why should the GF be tagged with any particular class?

Basically, inheritance :-)

(defclass pos3 ()
  ((x :accessor pos3-x)
   (y :accessor pos3-y)
   (z :accessor pos3-z)))

(defclass pos2 ()
  ((x :accessor pos2-x)
   (y :accessor pos2-y)))

(defclass house (pos3)
  ())

(defclass bullet (pos3)
  ())

(defclass text (pos2)
  ())

as opposed to

(defclass house ()
  ((x :accessor x)
   (y :accessor y)
   (z :accessor z)))

(defclass bullet ()
  ((x :accessor x)
   (y :accessor y)
   (z :accessor z)))

(defclass text ()
  ((x :accessor x)
   (y :accessor y)))

followed by

(defun some-3d-func-that-ignores-z(some-3d-object-list)
    (....
      (mapcar (lambda (obj)
                 ([x or pos3-x] obj...

If I have a bug where text ended up in my list,
I'll get an error with pos3-x but not with x.
and I don't have to worry about delimiting
it with package specifiers or shadowing.

--
Geoff
From: Tim Bradshaw
Subject: Re: Slot accessors and syntax
Date: 
Message-ID: <ey3vfb2v1mu.fsf@cley.com>
* Geoffrey Summerhayes wrote:

> Basically, inheritance :-)

I can't work out what your example is meant to mean.  It certainly is
not a good example of why accessors should be tagged by class, which
they should not be, of course.  You should tag accessors by what they
*do* (by their role in a protocol in other words).

--tim
From: Geoffrey Summerhayes
Subject: Re: Slot accessors and syntax
Date: 
Message-ID: <m1lwd.25095$%p1.1538884@news20.bellglobal.com>
"Tim Bradshaw" <···@cley.com> wrote in message 
····················@cley.com...
>* Geoffrey Summerhayes wrote:
>
>> Basically, inheritance :-)
>
> I can't work out what your example is meant to mean.  It certainly is
> not a good example of why accessors should be tagged by class, which
> they should not be, of course.  You should tag accessors by what they
> *do* (by their role in a protocol in other words).

Basically it's a way of preventing objects outside of a class hierarchy
being mistakenly accessed by the hierarchy's protocol. It's certainly
not the only way to handle it, you can be careful about the names you
select to make sure you don't get collisions, distinguish objects with
a "higher" method so you know the class you're dealing with, use explicit
packaging, or not worry about it at all.

Most involve extra typing, naming does give a slight APROPOS advantage
when you know the class but can't remember the method name.

--
Geoff 
From: Tim Bradshaw
Subject: Re: Slot accessors and syntax
Date: 
Message-ID: <ey3acsdus08.fsf@cley.com>
* Geoffrey Summerhayes wrote:
> Basically it's a way of preventing objects outside of a class hierarchy
> being mistakenly accessed by the hierarchy's protocol. 

Or in other words: object which don't follow a protocol partaking in
it.  Yes. So tag by *protocol* not some implementation detail.

--tim
From: Geoffrey Summerhayes
Subject: Re: Slot accessors and syntax
Date: 
Message-ID: <yvFwd.18194$pb.1231779@news20.bellglobal.com>
"Tim Bradshaw" <···@cley.com> wrote in message 
····················@cley.com...
>* Geoffrey Summerhayes wrote:
>> Basically it's a way of preventing objects outside of a class hierarchy
>> being mistakenly accessed by the hierarchy's protocol.
>
> Or in other words: object which don't follow a protocol partaking in
> it.  Yes. So tag by *protocol* not some implementation detail.


Good enough, now all I have to do is convince you
that the exposed class interface defines a protocol :-)

--
Geoff 
From: Tim Bradshaw
Subject: Re: Slot accessors and syntax
Date: 
Message-ID: <ey3pt0mu1cs.fsf@cley.com>
* Geoffrey Summerhayes wrote:

> Good enough, now all I have to do is convince you
> that the exposed class interface defines a protocol :-)

It may do.  It definitely does not if there are exposed slot names.

--tim
From: Mike Ajemian
Subject: Re: Slot accessors and syntax
Date: 
Message-ID: <AhLwd.458$fe5.1@trndny06>
"Geoffrey Summerhayes" <·············@hotmail.com> wrote in message
··························@news20.bellglobal.com...
>
> "Zach Beane" <····@xach.com> wrote in message
> ···················@unnamed.xach.com...
> > "Geoffrey Summerhayes" <·······@NhOoStPmAaMil.com> writes:
> >
> >> The next problem is naming accessors, a.y works because 'a' clarifies
> >> which 'y'. In Lisp it doesn't work that way. (<name> :accessor <name>)
> >> has a problem when name is a symbol like 'list'. So we generally use
> >> (<name> :accessor <class>-<name>)
> >
> > Speak for yourself! I think <class>-<name> is a really bad idea in the
> > presence of inheritance and generic functions. Someone else in this
> > thread mentioned shadowing, and I think that's a preferable way to
> > manage conflicts with other CL symbols. Someone else (perhaps the same
> > person) mentioned that both a house and a bullet may have a location;
> > why should the GF be tagged with any particular class?
>
> Basically, inheritance :-)
>
> (defclass pos3 ()
>   ((x :accessor pos3-x)
>    (y :accessor pos3-y)
>    (z :accessor pos3-z)))
>
> (defclass pos2 ()
>   ((x :accessor pos2-x)
>    (y :accessor pos2-y)))
>

(defclass pt ()
    ((x :accessor x :initform 1)
     (y :accessor y :initform 1)))
(defclass pt-3d (pt)
    ((z :accessor z :initform 1)))

(defmethod offset :before ((p1 pt) (p2 pt))
    (setf (x p1) (+ (x p1) (x p2)))
    (setf (y p1) (+ (y p1) (y p2))))
(defmethod offset :before ((p1 pt-3d) (p2 pt-3d))
    (setf (z p1) (+ (z p1) (z p2))))
(defmethod offset ((p1 pt-3d) (p2 pt-3d))
    t)
(defmethod offset ((p1 pt) (p2 pt))
    (error "processing PT instances not allowed in this context."))

(apply #'offset (mapcar #'make-instance '(pt-3d pt-3d))) ;; works
(apply #'offset (mapcar #'make-instance '(pt pt)))       ;; reports error

> (defun some-3d-func-that-ignores-z(some-3d-object-list)
>     (....
>       (mapcar (lambda (obj)
>                  ([x or pos3-x] obj...
>
> If I have a bug where text ended up in my list,
> I'll get an error with pos3-x but not with x.
> and I don't have to worry about delimiting
> it with package specifiers or shadowing.

By defining a primary method dispatching on 'pt (your pos2) that throws,
assert's or error's, you could restrict valid processing to a pt-3d (pos3)
and catch offending types (see 'offset dispatching on 'pt above). This
accomplishes your desired objective without duplicating class structure or
introducing namespace polution.

Mike
From: Geoffrey Summerhayes
Subject: Re: Slot accessors and syntax
Date: 
Message-ID: <KP8xd.41653$%p1.2345671@news20.bellglobal.com>
"Mike Ajemian" <············@verizon.net> wrote in message ····················@trndny06...
>
>
> (defclass pt ()
>    ((x :accessor x :initform 1)
>     (y :accessor y :initform 1)))
> (defclass pt-3d (pt)
>    ((z :accessor z :initform 1)))
>
> (defmethod offset :before ((p1 pt) (p2 pt))
>    (setf (x p1) (+ (x p1) (x p2)))
>    (setf (y p1) (+ (y p1) (y p2))))
> (defmethod offset :before ((p1 pt-3d) (p2 pt-3d))
>    (setf (z p1) (+ (z p1) (z p2))))
> (defmethod offset ((p1 pt-3d) (p2 pt-3d))
>    t)
> (defmethod offset ((p1 pt) (p2 pt))
>    (error "processing PT instances not allowed in this context."))
>
> (apply #'offset (mapcar #'make-instance '(pt-3d pt-3d))) ;; works
> (apply #'offset (mapcar #'make-instance '(pt pt)))       ;; reports error
>
>> (defun some-3d-func-that-ignores-z(some-3d-object-list)
>>     (....
>>       (mapcar (lambda (obj)
>>                  ([x or pos3-x] obj...
>>
>> If I have a bug where text ended up in my list,
>> I'll get an error with pos3-x but not with x.
>> and I don't have to worry about delimiting
>> it with package specifiers or shadowing.
>
> By defining a primary method dispatching on 'pt (your pos2) that throws,
> assert's or error's, you could restrict valid processing to a pt-3d (pos3)
> and catch offending types (see 'offset dispatching on 'pt above). This
> accomplishes your desired objective without duplicating class structure or
> introducing namespace polution.
>

Sure. As I mentioned in a previous post one of the other ways
of handling it is to differentiate the objects in a higher
method so you know which object you are dealing with.

But this code is a disaster. First a simple

(defmethod offset-2 ((p1 pt-3d)(p2 pt-3d))
  (incf (x p1) (x p2))
  (incf (y p1) (y p2))
  (incf (z p1) (z p2)))

will do, trying to process anything else will give a no
applicable method error.

Secondly if you do run this code, look what happens when it
triggers...

(defparameter a (make-instance 'pt))

> (setf a (make-instance 'pt))
A

> (describe a)

#<PT 20675744> is a PT
X      1
Y      1

> (offset a (make-instance 'pt))

Error: processing PT instances not allowed in this context.
  <snip>

CL-USER 22 : 2 > (describe a)

#<PT 20675744> is a PT
X      2
Y      2

The code just goes ahead and blindly alters the object
then decides it's the wrong one, leaving you to try and
manually reset the object. :AFTER not :BEFORE

In the opposite case, where OFFSET operates on the class
pt but not pt-3d, it makes sense:

(defmethod offset-3 :before ((p1 pt-3d)(p2 pt-3d))
    (error "processing PT-3D instances not allowed in this context."))

(defmethod offset-3 ((p1 pt)(p2 pt))
  (incf (x p1) (x p2))
  (incf (y p1) (y p2)))

Now the :BEFORE method catches the incorrect call before
mangling the object.

But we still have a problem, it only catches offsets when we pass
pt-3d for both arguments.

(defmethod offset-4 :before ((p1 pt)(p2 pt-3d))
    (error "processing PT-3D instances not allowed in this context."))

(defmethod offset-4 :before ((p1 pt-3d)(p2 pt))
    (error "processing PT-3D instances not allowed in this context."))

(defmethod offset-4 ((p1 pt)(p2 pt))
  (incf (x p1) (x p2))
  (incf (y p1) (y p2)))

But this would not be necessary if, as in the original post, PT was
not a subclass of PT-3D then a version of OFFSET-2 would suffice.
In a game with 3d objects and a 2d HUD for example, it's unlikely
there would be enough in common to merit relating them to each other.

-- 
Geoff

     "And then there's the code to consider."
     "You're pirates. Hang the code and hang the rules,
      they're more like guidelines anyway."
     - Pirates of the Caribbean
From: Mike Ajemian
Subject: Re: Slot accessors and syntax
Date: 
Message-ID: <okpxd.4065$152.267@trndny01>
"Geoffrey Summerhayes" <·······@NhOoStPmAaMil.com> wrote in message
····························@news20.bellglobal.com...
>
> "Mike Ajemian" <············@verizon.net> wrote in message
····················@trndny06...
> >
<snip code repeated below>
> >
>
> Sure. As I mentioned in a previous post one of the other ways
> of handling it is to differentiate the objects in a higher
> method so you know which object you are dealing with.
>
> But this code is a disaster. First a simple

Um, the code has a bug - it should modify structure in :after methods.
Simple mistake.

> (defmethod offset-2 ((p1 pt-3d)(p2 pt-3d))
>   (incf (x p1) (x p2))
>   (incf (y p1) (y p2))
>   (incf (z p1) (z p2)))
>
> will do, trying to process anything else will give a no
> applicable method error.

I fail to understand your point. Perhaps you could try with a different
example? The pos2 example, in my experience creates duplication of function
for cartesian coordinate systems. Where is the advantage?

> Secondly if you do run this code, look what happens when it
> triggers...
>
> (defparameter a (make-instance 'pt))
>
> > (setf a (make-instance 'pt))
> A
>
> > (describe a)
>
> #<PT 20675744> is a PT
> X      1
> Y      1
>
> > (offset a (make-instance 'pt))
>
> Error: processing PT instances not allowed in this context.
>   <snip>
>
> CL-USER 22 : 2 > (describe a)
>
> #<PT 20675744> is a PT
> X      2
> Y      2
>
> The code just goes ahead and blindly alters the object
> then decides it's the wrong one, leaving you to try and
> manually reset the object. :AFTER not :BEFORE

Right, my mistake. This is fixed by changing the :before methods to :after
methods so there is no modification of data prior to validation of types.

> In the opposite case, where OFFSET operates on the class
> pt but not pt-3d, it makes sense:
>
> (defmethod offset-3 :before ((p1 pt-3d)(p2 pt-3d))
>     (error "processing PT-3D instances not allowed in this context."))
>
> (defmethod offset-3 ((p1 pt)(p2 pt))
>   (incf (x p1) (x p2))
>   (incf (y p1) (y p2)))
>
> Now the :BEFORE method catches the incorrect call before
> mangling the object.
>
> But we still have a problem, it only catches offsets when we pass
> pt-3d for both arguments.
>
> (defmethod offset-4 :before ((p1 pt)(p2 pt-3d))
>     (error "processing PT-3D instances not allowed in this context."))
>
> (defmethod offset-4 :before ((p1 pt-3d)(p2 pt))
>     (error "processing PT-3D instances not allowed in this context."))
>
> (defmethod offset-4 ((p1 pt)(p2 pt))
>   (incf (x p1) (x p2))
>   (incf (y p1) (y p2)))
>
> But this would not be necessary if, as in the original post, PT was
> not a subclass of PT-3D then a version of OFFSET-2 would suffice.
> In a game with 3d objects and a 2d HUD for example, it's unlikely
> there would be enough in common to merit relating them to each other.
>
> -- 
> Geoff

The point model I'm presenting is one I used for a medical CAD app using GL.
Data had to be passed between 2D and 3D devices and external libraries. It
was important to use the shared structure to reduce code size and
duplication of effort (fixing a bug in a 2D function benefits the 3D
function). YMMV of course.

Using :before methods to catch errors is a bad idea as all :before methods
are evaluated for a given hierarchy. In a class with shared hierarchy, an
error might inadvertently be presented. Then again, I guess that's the point
you're making? By using the primary methods to catch errors you're
guaranteed that a dispatch to an unwelcome type will be managed correctly
(this requires primary methods for each, or enough for common types). My
mistake of modifying data in the :before methods was an error, but not the
use of primary methods as the error catching method. Here's a better
version:

(defclass pt ()
   ((x :accessor x :initform 1)
    (y :accessor y :initform 1)))
(defclass pt-3d (pt)
   ((z :accessor z :initform 1)))

(defmethod offset ((p1 pt-3d) (p2 pt-3d))
   t)
(defmethod offset ((p1 pt) (p2 pt))
   (error "processing PT instances not allowed in this context."))

(defmethod offset :after ((p1 pt) (p2 pt))
   (setf (x p1) (+ (x p1) (x p2)))
   (setf (y p1) (+ (y p1) (y p2))))
(defmethod offset :after ((p1 pt-3d) (p2 pt-3d))
   (setf (z p1) (+ (z p1) (z p2))))

(apply #'offset (mapcar #'make-instance '(pt-3d pt-3d))) ;; works
(apply #'offset (mapcar #'make-instance '(pt pt)))       ;; reports error

But from going back and reading your original posts, I can't tell whether
you already understand all this or not.

Mike
From: Geoffrey Summerhayes
Subject: Re: Slot accessors and syntax
Date: 
Message-ID: <xPuxd.5461$Z%3.225395@news20.bellglobal.com>
"Mike Ajemian" <············@verizon.net> wrote in message ·······················@trndny01...
>
> The point model I'm presenting is one I used for a medical CAD app using GL.
> Data had to be passed between 2D and 3D devices and external libraries. It
> was important to use the shared structure to reduce code size and
> duplication of effort (fixing a bug in a 2D function benefits the 3D
> function). YMMV of course.

Well, it was meant to be an example of distinct classes that
ended up accidently sharing slot names but little, if any,
behaviour. Expounding a little more

(defclass pt-screen-coordinate ()
   ((x :accessor x :type integer :initform 0)
    (y :accessor y :type integer :initform 480)))

(defclass pt-world-coordinate ()
   ((x :accessor x :type double-float :initform 0.0d0)
    (y :accessor y :type double-float :initform 0.0d0)
    (z :accessor z :type double-float :initform 0.0d0)))

(defclass window (pt-screen-coordinate)
   ...)
(defclass text (pt-screen-coordinate)
   ...)
(defclass sphere (pt-world-coordinate)
   ...)
(defclass cube (pt-world-coordinate)
   ...)

And catching the error if an instance ended up in the wrong place, in this
case signalling an error and failing to corrupt the data or present incorrect
information. i.e.

(defun map-points (3d-list colour)
  (mapc (lambda (instance)
          (ignore-errors
            (draw-point ...
                        (... (x instance))
                        (... (y instance))
                        colour)))
        3d-list))
...
(map-points sphere-list :red)
(map-points text-list :red) ; oops
(map-points cube-list :red)
(draw-text text-list)
...

That's why we were talking about packaging, naming conventions, etc.

> Using :before methods to catch errors is a bad idea as all :before methods
> are evaluated for a given hierarchy. In a class with shared hierarchy, an
> error might inadvertently be presented. Then again, I guess that's the point
> you're making?

Actually, I've never had the misfortune of working with a hierarchy designed
so there were methods that were not inherited by a subclass. Or even worse,
were inherited by a subclass of a restricted subclass.

--
Geoff
From: Mike Ajemian
Subject: Re: Slot accessors and syntax
Date: 
Message-ID: <txOxd.4894$152.868@trndny01>
"Geoffrey Summerhayes" <·······@NhOoStPmAaMil.com> wrote in message
··························@news20.bellglobal.com...
>
> "Mike Ajemian" <············@verizon.net> wrote in message
·······················@trndny01...
> >
> > The point model I'm presenting is one I used for a medical CAD app using
GL.
> > Data had to be passed between 2D and 3D devices and external libraries.
It
> > was important to use the shared structure to reduce code size and
> > duplication of effort (fixing a bug in a 2D function benefits the 3D
> > function). YMMV of course.
>
> Well, it was meant to be an example of distinct classes that
> ended up accidently sharing slot names but little, if any,
> behaviour. Expounding a little more
>
> (defclass pt-screen-coordinate ()
>    ((x :accessor x :type integer :initform 0)
>     (y :accessor y :type integer :initform 480)))
>
> (defclass pt-world-coordinate ()
>    ((x :accessor x :type double-float :initform 0.0d0)
>     (y :accessor y :type double-float :initform 0.0d0)
>     (z :accessor z :type double-float :initform 0.0d0)))
>
> (defclass window (pt-screen-coordinate)
>    ...)
> (defclass text (pt-screen-coordinate)
>    ...)
> (defclass sphere (pt-world-coordinate)
>    ...)
> (defclass cube (pt-world-coordinate)
>    ...)
>
> And catching the error if an instance ended up in the wrong place, in this
> case signalling an error and failing to corrupt the data or present
incorrect
> information. i.e.
>
> (defun map-points (3d-list colour)
>   (mapc (lambda (instance)
>           (ignore-errors
>             (draw-point ...
>                         (... (x instance))
>                         (... (y instance))
>                         colour)))
>         3d-list))
> ...
> (map-points sphere-list :red)
> (map-points text-list :red) ; oops
> (map-points cube-list :red)
> (draw-text text-list)
> ...
>

Ok. I see what you're saying. You're having a problem with namespaces -
preventing collisions. You're right, the namespace issues are easier to run
into with Lisp. The fact remains that by using defclass you create distinct
type information you can use to control execution in a given context. By
ignoring the type information you lose any advantage oop offers. In the code
above, draw-point could be a generic method which dispatches by type and
then provides a context for calling the accessors. That's similar to other
oop languages.

I made a bonehead mistake in my previous code example. Instead of creating a
series of methods to catch errors by specific type, you could create a
single method to catch errors that doesn't declare types in the signature:

(defmethod offset (a b)
   (error "Error - can only dispatch on type PT for method OFFSET"))

This version of 'offset becomes a catch-all for types that aren't allowed
for a context.

> That's why we were talking about packaging, naming conventions, etc.
>
> > Using :before methods to catch errors is a bad idea as all :before
methods
> > are evaluated for a given hierarchy. In a class with shared hierarchy,
an
> > error might inadvertently be presented. Then again, I guess that's the
point
> > you're making?
>
> Actually, I've never had the misfortune of working with a hierarchy
designed
> so there were methods that were not inherited by a subclass. Or even
worse,
> were inherited by a subclass of a restricted subclass.
>
> --
> Geoff
>
>

We should all have such misfortune! After spending years slogging C++/Java,
I'll take Lisp any day. In my experience, namespace problems arise rarely.
When they do, you can a) find solutions to work around the problem or b)
build a solution that can extend/change the language.

Mike
From: Geoffrey Summerhayes
Subject: Re: Slot accessors and syntax
Date: 
Message-ID: <PSQxd.12264$GK5.812487@news20.bellglobal.com>
"Mike Ajemian" <············@verizon.net> wrote in message ·······················@trndny01...
>
> "Geoffrey Summerhayes" <·······@NhOoStPmAaMil.com> wrote in message
> ··························@news20.bellglobal.com...
>>
>> ...
>>
>
> Ok. I see what you're saying. You're having a problem with namespaces -
> preventing collisions. You're right, the namespace issues are easier to run
> into with Lisp. The fact remains that by using defclass you create distinct
> type information you can use to control execution in a given context. By
> ignoring the type information you lose any advantage oop offers. In the code
> above, draw-point could be a generic method which dispatches by type and
> then provides a context for calling the accessors. That's similar to other
> oop languages.

At a cost though, assuming draw-point is a low-level member of the gui library
we're introducing yet-another-method to distinguish. Simpler would be to
adopt a naming convention where the collision wouldn't occur or package
them in different places. i.e (pt:x instance) vs. (pt3d:x instance)

> I made a bonehead mistake in my previous code example. Instead of creating a
> series of methods to catch errors by specific type, you could create a
> single method to catch errors that doesn't declare types in the signature:
>
> (defmethod offset (a b)
>   (error "Error - can only dispatch on type PT for method OFFSET"))
>
> This version of 'offset becomes a catch-all for types that aren't allowed
> for a context.

I've been wondering about this, you do realize the default behaviour
when Lisp cannot find an applicable primary method *is* to signal an
error? That's why I just had:

;; this will reject pt instances no applicable method
(defmethod offset-2 ((p1 pt-3d)(p2 pt-3d))
  (incf (x p1) (x p2))
  (incf (y p1) (y p2))
  (incf (z p1) (z p2)))

>> That's why we were talking about packaging, naming conventions, etc.
>>
>> > Using :before methods to catch errors is a bad idea as all :before
> methods
>> > are evaluated for a given hierarchy. In a class with shared hierarchy,
> an
>> > error might inadvertently be presented. Then again, I guess that's the
> point
>> > you're making?
>>
>> Actually, I've never had the misfortune of working with a hierarchy
> designed
>> so there were methods that were not inherited by a subclass. Or even
> worse,
>> were inherited by a subclass of a restricted subclass.
>>
>
> We should all have such misfortune! After spending years slogging C++/Java,
> I'll take Lisp any day. In my experience, namespace problems arise rarely.
> When they do, you can a) find solutions to work around the problem or b)
> build a solution that can extend/change the language.
>

Lisp does allow for a wide variety of styles.
But as far as the misfortune I was referring to, the opposite case
from the example above, I'd just change the hierarchy:

(defclass common-pt ()
   ((x :accessor x :initform 1)
    (y :accessor y :initform 1)))
(defclass pt-3d (common-pt)
   ((z :accessor z :initform 1)))
(defclass pt (common-pt)())

;; this will reject pt-3d instances
(defmethod offset-5 ((p1 pt)(p2 pt))
  (incf (x p1) (x p2))
  (incf (y p1) (y p2)))

;; this will accept both pt and pt-3d instances
(defmethod offset-6 ((p1 common-pt)(p2 common-pt))
  (incf (x p1) (x p2))
  (incf (y p1) (y p2)))

Speaking of style, at the office we prefer primary methods
to do the actual work/modifications, that way we can write

(defmethod foo((bar bar)...)
   ;; before superclass modification
   (let ((result nil))
     (when (next-method-p)
       (setf result (call-next-method bar ...)))
     ;; any changes to superclass are done and can be referenced
     modified-result))


Use CLOS itself for type management and dispatching and keep
before, after, and around for resource management, reporting,
debugging, etc.
YMMV

--
Geoff
From: Mike Ajemian
Subject: Re: Slot accessors and syntax
Date: 
Message-ID: <JG%xd.4062$vF5.4002@trndny07>
"Geoffrey Summerhayes" <·······@NhOoStPmAaMil.com> wrote in message
···························@news20.bellglobal.com...
>
> "Mike Ajemian" <············@verizon.net> wrote in message
·······················@trndny01...
> >
> > "Geoffrey Summerhayes" <·······@NhOoStPmAaMil.com> wrote in message
> > ··························@news20.bellglobal.com...
> >>
> >> ...
> >>
> >
> > Ok. I see what you're saying. You're having a problem with namespaces -
> > preventing collisions. You're right, the namespace issues are easier to
run
> > into with Lisp. The fact remains that by using defclass you create
distinct
> > type information you can use to control execution in a given context. By
> > ignoring the type information you lose any advantage oop offers. In the
code
> > above, draw-point could be a generic method which dispatches by type and
> > then provides a context for calling the accessors. That's similar to
other
> > oop languages.
>
> At a cost though, assuming draw-point is a low-level member of the gui
library
> we're introducing yet-another-method to distinguish. Simpler would be to
> adopt a naming convention where the collision wouldn't occur or package
> them in different places. i.e (pt:x instance) vs. (pt3d:x instance)

I suspected this was about performance :) Having seen years of early
optimization and the lost time associated with it, I'm very wary of trading
expressiveness and simplicity for greater complexity. Unless profiling
proves the need.

> > I made a bonehead mistake in my previous code example. Instead of
creating a
> > series of methods to catch errors by specific type, you could create a
> > single method to catch errors that doesn't declare types in the
signature:
> >
> > (defmethod offset (a b)
> >   (error "Error - can only dispatch on type PT for method OFFSET"))
> >
> > This version of 'offset becomes a catch-all for types that aren't
allowed
> > for a context.
>
> I've been wondering about this, you do realize the default behaviour
> when Lisp cannot find an applicable primary method *is* to signal an
> error? That's why I just had:

I know what you're saying. You're describing leaving a latent bug in program
code to be found later (or not). I've seen it and done it a million times -
just get the feature working, we'll find the bugs and fix 'em later. Instead
of relying on the repl, play defense for a minute. Build a stub, function or
method that serves notice of inconsistent application state but doesn't
interrupt flow.

> ;; this will reject pt instances no applicable method
> (defmethod offset-2 ((p1 pt-3d)(p2 pt-3d))
>   (incf (x p1) (x p2))
>   (incf (y p1) (y p2))
>   (incf (z p1) (z p2)))
>
<snip>
>
> Lisp does allow for a wide variety of styles.
> But as far as the misfortune I was referring to, the opposite case
> from the example above, I'd just change the hierarchy:
>
> (defclass common-pt ()
>    ((x :accessor x :initform 1)
>     (y :accessor y :initform 1)))
> (defclass pt-3d (common-pt)
>    ((z :accessor z :initform 1)))
> (defclass pt (common-pt)())
>
> ;; this will reject pt-3d instances
> (defmethod offset-5 ((p1 pt)(p2 pt))
>   (incf (x p1) (x p2))
>   (incf (y p1) (y p2)))
>
> ;; this will accept both pt and pt-3d instances
> (defmethod offset-6 ((p1 common-pt)(p2 common-pt))
>   (incf (x p1) (x p2))
>   (incf (y p1) (y p2)))
>

Right. Misfortune. Why not add the subclass to close the hierarchy and make
a diamond? Yes this is a problem of oop. The problem isn't 'offset-5, it's
'offset-6 which needs to specialize on 'pt-3d (because of the class design).
Both subclasses of 'common-pt need methods and each would need to rely on a
'common-pt primary method to set slots in 'common-pt instances. The
programmer has to place 'call-next-method in the right place in the primary
methods to call the 'common-pt method (if the methods were called 'offset).
Or the code can just be a function that tests the type of object passed and
alters data accordingly. Whatever works for you. Oop's got some rules and ya
 can't get sloppy with 'em even if you don't like 'em. Caveat emptor. This
isn't an oop problem, at least not in Lisp.

> Speaking of style, at the office we prefer primary methods
> to do the actual work/modifications, that way we can write
>
> (defmethod foo((bar bar)...)
>    ;; before superclass modification
>    (let ((result nil))
>      (when (next-method-p)
>        (setf result (call-next-method bar ...)))
>      ;; any changes to superclass are done and can be referenced
>      modified-result))
>
>
> Use CLOS itself for type management and dispatching and keep
> before, after, and around for resource management, reporting,
> debugging, etc.
> YMMV
>
> --
> Geoff

I got your primary style right here, buddy. Heh, heh! Primary methods are
alright. I also structure classes to take advantage of dispatch in :before
and :after methods where I can, as in the previous examples. It's less
trouble than micro-managing primary method dispatch sometimes. I use primary
methods, but sometimes wish I had more control over managing dispatch to
other primary methods for a given condition. Can be a real pain sometimes
getting the sequencing right, especially when it needs to vary.

Mike
From: Geoffrey Summerhayes
Subject: Re: Slot accessors and syntax
Date: 
Message-ID: <pNayd.14827$GK5.1098679@news20.bellglobal.com>
"Mike Ajemian" <············@verizon.net> wrote in message ························@trndny07...
> "Geoffrey Summerhayes" <·······@NhOoStPmAaMil.com> wrote in message
> ···························@news20.bellglobal.com...

<snip>

> I suspected this was about performance :) Having seen years of early
> optimization and the lost time associated with it, I'm very wary of trading
> expressiveness and simplicity for greater complexity. Unless profiling
> proves the need.

No, I just feel it's damn silly to write two methods to
only accept one class, if it accepts pt and pt-3d then
you're back to square one. Naming or packaging will help
prevent the problem, but if you want to play it even safer

(check-type instance pt3d:pt-3d)

<snip>

> I know what you're saying. You're describing leaving a latent bug in program
> code to be found later (or not). I've seen it and done it a million times -
> just get the feature working, we'll find the bugs and fix 'em later. Instead
> of relying on the repl, play defense for a minute. Build a stub, function or
> method that serves notice of inconsistent application state but doesn't
> interrupt flow.

ROTFLMAO

Yeah, you never know when someone is going to come along and
absent-mindedly type:

(defmethod no-applicable-method ((fn (eql #'offset)) &rest args))

and accidently turn off that unreliable built-in. :-P

You might as well take it one step further, shadow half of the CL
package and stick in type checking just in case someone changes the
compiler safety settings.

 <snip>

>>
>> Lisp does allow for a wide variety of styles.
>> But as far as the misfortune I was referring to, the opposite case
>> from the example above, I'd just change the hierarchy:
>>
>> (defclass common-pt ()
>>    ((x :accessor x :initform 1)
>>     (y :accessor y :initform 1)))
>> (defclass pt-3d (common-pt)
>>    ((z :accessor z :initform 1)))
>> (defclass pt (common-pt)())
>>
>> ;; this will reject pt-3d instances
>> (defmethod offset-5 ((p1 pt)(p2 pt))
>>   (incf (x p1) (x p2))
>>   (incf (y p1) (y p2)))
>>
>> ;; this will accept both pt and pt-3d instances
>> (defmethod offset-6 ((p1 common-pt)(p2 common-pt))
>>   (incf (x p1) (x p2))
>>   (incf (y p1) (y p2)))
>>
>
> Right. Misfortune. Why not add the subclass to close the hierarchy and make
> a diamond? Yes this is a problem of oop. The problem isn't 'offset-5, it's
> 'offset-6 which needs to specialize on 'pt-3d (because of the class design).
> Both subclasses of 'common-pt need methods and each would need to rely on a
> 'common-pt primary method to set slots in 'common-pt instances. The
> programmer has to place 'call-next-method in the right place in the primary
> methods to call the 'common-pt method (if the methods were called 'offset).
> Or the code can just be a function that tests the type of object passed and
> alters data accordingly. Whatever works for you. Oop's got some rules and ya
> can't get sloppy with 'em even if you don't like 'em. Caveat emptor. This
> isn't an oop problem, at least not in Lisp.

Here I just don't see your point. Inheritance and specialization
are the nature of the beast whether through primary or after methods.

<snip>

> I got your primary style right here, buddy. Heh, heh! Primary methods are
> alright. I also structure classes to take advantage of dispatch in :before
> and :after methods where I can, as in the previous examples. It's less
> trouble than micro-managing primary method dispatch sometimes. I use primary
> methods, but sometimes wish I had more control over managing dispatch to
> other primary methods for a given condition. Can be a real pain sometimes
> getting the sequencing right, especially when it needs to vary.
>

Tastes vary.

Speaking of debugging, if I'm worried that my offset methods are generating
objects in an incorrect state somewhere in the code, I can just write

;; requires validation methods, of course
(defmethod offset :after (a b)
  (when (not (valid-object-p a))
    (error "found one!")))

How are you going to do it? At this point I'm done, your code hasn't
even started. You've got my primary style right there, buuuuuudy.

--
Geoff 
From: Mike Ajemian
Subject: Re: Slot accessors and syntax
Date: 
Message-ID: <0WFyd.8004$vF5.6008@trndny07>
"Geoffrey Summerhayes" <·······@NhOoStPmAaMil.com> wrote in message
····························@news20.bellglobal.com...
>
> "Mike Ajemian" <············@verizon.net> wrote in message
························@trndny07...
> > "Geoffrey Summerhayes" <·······@NhOoStPmAaMil.com> wrote in message
> > ···························@news20.bellglobal.com...
>
> <snip>
>
> > I suspected this was about performance :) Having seen years of early
> > optimization and the lost time associated with it, I'm very wary of
trading
> > expressiveness and simplicity for greater complexity. Unless profiling
> > proves the need.
>
> No, I just feel it's damn silly to write two methods to
> only accept one class, if it accepts pt and pt-3d then
> you're back to square one. Naming or packaging will help
> prevent the problem, but if you want to play it even safer

You lost me here. What two methods are you talking about. I thought you
could pass a list of text instances to your function and that they could
cause the accessor problem? The change I advocated to 'draw-point was a
dispatch method by type. That's two classes and two methods (one to process,
the other to catch errors/log). But it eliminates the need to workaround
slot
accessor names. What am I missing?

> (check-type instance pt3d:pt-3d)
>
> <snip>
>
> > I know what you're saying. You're describing leaving a latent bug in
program
> > code to be found later (or not). I've seen it and done it a million
times -
> > just get the feature working, we'll find the bugs and fix 'em later.
Instead
> > of relying on the repl, play defense for a minute. Build a stub,
function or
> > method that serves notice of inconsistent application state but doesn't
> > interrupt flow.
>
> ROTFLMAO
>
> Yeah, you never know when someone is going to come along and
> absent-mindedly type:
>
> (defmethod no-applicable-method ((fn (eql #'offset)) &rest args))
>
> and accidently turn off that unreliable built-in. :-P
>
> You might as well take it one step further, shadow half of the CL
> package and stick in type checking just in case someone changes the
> compiler safety settings.

WTF? If you handle this in a function rather than relying on the repl, the
program doesn't have to stop. If you pass a 'text instance to a function
that can't handle it, that event could be logged and the log later reviewed.
No program interruption.

>  <snip>
>
<snip>
>
> Here I just don't see your point. Inheritance and specialization
> are the nature of the beast whether through primary or after methods.

I think the point was that the OOP features you describe are pretty much
core to other languages. Lisp is such a dynamic language that creating a
rigid structure that can't be modified at runtime is just glaring to me. Not
to say that fixing the problem isn't *really hard*. Just that what is a
feature in C++/Java may just be a "bug" in Lisp.

> <snip>
>
> > I got your primary style right here, buddy. Heh, heh! Primary methods
are
> > alright. I also structure classes to take advantage of dispatch in
:before
> > and :after methods where I can, as in the previous examples. It's less
> > trouble than micro-managing primary method dispatch sometimes. I use
primary
> > methods, but sometimes wish I had more control over managing dispatch to
> > other primary methods for a given condition. Can be a real pain
sometimes
> > getting the sequencing right, especially when it needs to vary.
> >
>
> Tastes vary.
>
> Speaking of debugging, if I'm worried that my offset methods are
generating
> objects in an incorrect state somewhere in the code, I can just write
>
> ;; requires validation methods, of course
> (defmethod offset :after (a b)
>   (when (not (valid-object-p a))
>     (error "found one!")))
>
> How are you going to do it? At this point I'm done, your code hasn't
> even started. You've got my primary style right there, buuuuuudy.
> --
> Geoff
>
>

Done? Not so fast, Junior G-man.

valid-object-p is a method specializing on instance 'a that validates the
internals? Makes great sense! In the other paradigm, the client of instance
'a performs the check in the :before method. More code for the general case,
but it's contextual - it becomes possible to specialize the validation of
generic types for specific functionality:

(defmethod foo :before ((a pt) ...)
   (valid-object-p a)
   )

(defmethod bar :before ((a pt) ...)
   ;; contextual check
   (assert (and (> (x pt) 0) (< 100 (x pt))) ...) ; custom validation of
general type
   ;; or reset internals if out-of-bounds, throw, log, whatever...
   ...)

Each of our solutions' has it's own set of problems.

Mike
From: Alan Crowe
Subject: Re: Slot accessors and syntax
Date: 
Message-ID: <86d5xbwxsg.fsf@cawtech.freeserve.co.uk>
Chris Capel pondered:
> There are the actions that are performed on the
> objects. And there's the internal state of the
> objects. Now, this third element has to be represented
> somehow as data that gets associated somehow with
> instances of each class of object. A slot.

Consider writing a thermodynamics program, with an object
that represents a fixed mass of gas. The gas has volume,
pressure, temperature, and entropy. We can compress it,
adiabatically or adiathermally. We can heat it, at constant
pressure or constant temperature.

It has two degrees of freedom, so there are six
natural possibilities for the slots:

1)Pressure and Volume
2)Pressure and Termperature
3)Pressure and Entropy
4)Volume and Temperature
5)Volume and Entropy
6)Temperature and Entropy

This choice is important to the implementation of the gas
object, but you do not want it to leak out into the
surrounding physics code.

Consider writing a robot arm program. You have 3D
rotations. You want to be able to compose them, and apply
them to coordinates. You want to be able to specify them in
a variety of ways. How are they implemented?

1)a 3x3 matrix
2)a complex 2x2 matrix
3)a unit quaternion
4)Euler's angles

You really want your 3D rotation object to be a leak-free
abstraction, because of the horrors that lurk beneath the
surface.

Chris Capel asks:

> What /is/ it that were you saying about protocols, in view
> of all this?

I cannot speak for Tim, but my feeling is that most code is
unlike my two examples. Most of the time there is an obvious
set of slots. In a policitcal program the president object
will have a slot for his party. How could it be otherwise?

So the question arises: is this a convenience or a trap?

Does thinking in terms of slots, rather than protocols,
train one in a dumbed down style of programming, that works
well enough most of the time, but leaves one floundering when
it comes to writing more sophisticated objects in which
the choice slots is unobvious?

I'm slowly working my way through The Structure and
Interpretation of Computer Programs. Abelson and Sussman
keep surprising me by finding more scope for abstraction
than I thought possible. I think there is a knack to
spotting abstractions, and I think I would profit if I
worked at cultivating it. I am hoping to learn to think in
protocols rather than slots.

Alan Crowe
Edinburgh
Scotland
From: Tim Bradshaw
Subject: Re: Slot accessors and syntax
Date: 
Message-ID: <ey3zn0fvcty.fsf@cley.com>
* Alan Crowe wrote:

> So the question arises: is this a convenience or a trap?

It's a trap.  If you expose slots you have two incompatible syntaxes
for things:

   * In lisp: (slot-value x slot-name) vs (accessor x)
   * In Java and similar x.foo vs x.foo().

Now you can never, ever, change your implementation.  To continue your
politician analogy: what was Winston Churchill's party?

   (slot-value winston 'party)

is not going to give you a useful answer.  Instead you need something
like:

   (party winston :when 1910)

And even if you don't need this, maybe politician objects fetch
information lazily from some backend database, so PARTY actually does
something like:

(defmethod party :around ((ob database-backed-object))
  (or (cache-value ob 'party)
      (setf (cache-value ob 'party) (call-next-method))))

Now of course, someone will say that you could do this using the MOP
to implement lazy slots which fetch their values from a database on
demand.  Well, yes. Kenny and I can end all arguments on cll using our
black helicopters, but we feel it is equally inappropriate.

There's an old joke about syntax and cancer.

--tim
From: Tim Bradshaw
Subject: Re: Slot accessors and syntax
Date: 
Message-ID: <ey38y7zwsnk.fsf@cley.com>
* Chris Capel wrote:
> What /is/ it that were you saying about protocols, in view of all this? I've
> presented my understanding of object-oriented abstraction. How is it
> different from what you've presented?

Well, let's say I want to deal with some kind of newtonian physics
system where I have things which have locations in 3space.  So, well,
how do I do this? What I do is I design a little protocol.  Here's a
first (partial) draft:

    PHYSICS-THING-P (THING): true if X is a physics thing
    X (THING): x coordinate of thing
    (SETF X) (NEW THING): set x coordinate
    ...

Note I haven't defined how to make these things, and I definitely
haven't defined how the data associated with a physics thing is held:
it might be slots or it might not.  Clearly there is some kind of slot
somewhere, but it's no business of any kind of client to know this.

I need a little implementation for this:

    (defgeneric physics-thing-p (thing)
      (:method ((thing t)) nil))
    (defgeneric x (thing))
    (defgeneric (setf x) (new thing))
    ...

Now I could create some implementation of this protocol, which might
define, say X as an accessor:

    (defclass simple-physics-thing ()
      ((x :initform 0.0 :accessor x)
       ...))
    (defmethod physics-thing-p ((thing simple-physics-thing)) t)
    ...

And I could have another one:

    (defclass vector-physics-thing ()
      ((v :initform (make-array 3 ...))))
    (defmethod x ((thing vector-physics-thing))
      (aref (slot-value thing 'v) 0))
    ...
    (defmethod physics-thing-p ((thing vector-physics-thing)) t)
    ...

This protocol is terribly simple - it's the sort of thing that a Java
interface can do as there's only one participating thing.  It's also
incomplete - how do I make these things, for instance?  But it is an
example.

Now I can realise that well, my protocol sucks (wrong names, should
expose vectors (but then issues: do I copy?), what about bases other
than cartesian? blah), and modify the protocol and then the various
implementations.

But the issue is that clients use the *protocol*, not the
implementation.  And the protocol should never be specified in terms
of slots, because once a client knows it can do SLOT-VALUE you have
effectively specified the implementation.  It is probably safe to
specify a protocol in terms of accessors, because it's likely that
WITH-ACCESSORS is just a wrapper around SYMBOL-MACROLET, but I think
that this would be very bad style just the same.

So slots are this little local thing which you need in the
implementation of a protocol.  You don't need to be stressing about
their names because they should (almost) never be exposed to clients.

Names of things in protocols however matter more.  Choosing good names
is always hard, of course.  The only thing I think is general is that
if you use object-op type names (foo-height) make sure that the object
part describes the role name in the protocol, not some specific type
which you will later regret.

--tim
From: Paolo Amoroso
Subject: Re: Slot accessors and syntax
Date: 
Message-ID: <87sm67z3eb.fsf@plato.moon.paoloamoroso.it>
Tim Bradshaw <···@cley.com> writes:

> Note I haven't defined how to make these things, and I definitely
[...]
> This protocol is terribly simple - it's the sort of thing that a Java
> interface can do as there's only one participating thing.  It's also
> incomplete - how do I make these things, for instance?  But it is an
[...]
> But the issue is that clients use the *protocol*, not the
> implementation.  And the protocol should never be specified in terms
> of slots, because once a client knows it can do SLOT-VALUE you have
> effectively specified the implementation.  It is probably safe to

Concerning the making things operation, what's your opinion on the
explicit constructor vs (make-instance 'foo-class) issue?  If I
understand correctly what you write, the use of a protocol seems to
rule out make-instance.


Paolo
-- 
Why Lisp? http://alu.cliki.net/RtL%20Highlight%20Film
Recommended Common Lisp libraries/tools (see also http://clrfi.alu.org):
- ASDF/ASDF-INSTALL: system building/installation
- CL-PPCRE: regular expressions
- UFFI: Foreign Function Interface
From: Tim Bradshaw
Subject: Re: Slot accessors and syntax
Date: 
Message-ID: <ey3r7lqv1ji.fsf@cley.com>
* Paolo Amoroso wrote:

> Concerning the making things operation, what's your opinion on the
> explicit constructor vs (make-instance 'foo-class) issue?  If I
> understand correctly what you write, the use of a protocol seems to
> rule out make-instance.

Well, make-instance is OK - I don't have any problem with knowing that
things were defined with DEFCLASS.  In practice I find that it is
normally useful to define constructors which have more intelligence
(better argument lists for instance).

--tim
From: Thomas A. Russ
Subject: Re: Slot accessors and syntax
Date: 
Message-ID: <ymiacsf57ik.fsf@sevak.isi.edu>
Chris Capel <······@iba.nktech.net> writes:

> Coming from C#, the most painful aspect of Lisp seems to me to be using
> objects. Yes, the system is more powerful. But the syntax just isn't made
> for it. When I have an object foo-inst of class foo, with slots bar and
> baz, here are my options for accessing the slots:
>
> 1) (slot-value foo-inst 'bar), (slot-value foo-inst 'baz)
> 2) (with-slots (bar baz) foo-inst ...)
>    Declaring accessors in my defclass,
> 3)   (foo.bar foo-inst), (foo.baz foo-inst)
>    and
> 4)   (with-accessors (foo.bar foo.baz) foo-inst ...)

The real answer is to move further away from C# and embrace the Lisp
way.  Seriously.  The object model is different from the one that C++,
Java and C# (among others share).

First of all, everything is an object of some sort.  One doesn't have
the distinction between primitive types and objects (well, mostly not,
but it will do for now).

Objects are acted upon.  Action takes place using functions.  Some
functions are generic and can act differently (via methods) depending on
the types of their arguments.

It just so happens that some generic functions are used to implement
slot access and (via SETF) modification.

But you have to think of DOING things in terms of functions and not
slots, etc.  The sorts of things you do are goverened by the functions
that are made available.  Those functions define the "protocol" for
interacting with objects in the world.

So, when you declare a class, you decide which slots will be accessible
via slot accessor functions.  You take a function-centric view of using
things.  It takes a little while to get used to the notion that slot
access happens via functions, but there is evidence through design
guides and patterns that the rest of the OO community is heading in this
direction.  In some programming shops, one is not allowed to directly
access slots from outside a class implementation.  One must use get- and
set- methods on the objects.

As others have pointed out, that indirection is what gives one the
flexibility to rearrange the internal details of class data storage, as
well as to provide additional flexibility to anyone who later
specializes (subclasses) an existing class.  This notion of a set of
related operations (generic functions) is a key one to grasp.  It is
also why you want to try to give names that reflect what it is that is
being provided, rather than the data types of the providers.  For that
reason, including the class name in the accessor is generally a bad
idea.  It can look a bit silly when using subclasses with different
names, and it makes it even worse if you ever decide that you need to
move one of those generic functions UP in the class hierarchy, because
then it applies to things that are not even instances of the name.
Better to stick with descriptive names, and use the package system to
sort out any potential name clashes.  (This latter solution isn't
perfect, so sometimes one does need to deviate, but...)


-- 
Thomas A. Russ,  USC/Information Sciences Institute
From: David Sletten
Subject: Re: Slot accessors and syntax
Date: 
Message-ID: <BwGvd.3095$gd.863@twister.socal.rr.com>
Chris Capel wrote:

> Coming from C#, the most painful aspect of Lisp seems to me to be using
> objects. Yes, the system is more powerful. But the syntax just isn't made
> for it. When I have an object foo-inst of class foo, with slots bar and
> baz, here are my options for accessing the slots:
> 
> 1) (slot-value foo-inst 'bar), (slot-value foo-inst 'baz)
> 2) (with-slots (bar baz) foo-inst ...)
>    Declaring accessors in my defclass,
> 3)   (foo.bar foo-inst), (foo.baz foo-inst)
>    and
> 4)   (with-accessors (foo.bar foo.baz) foo-inst ...)
> 
> I've heard it recommended that one use accessors preferably to slots, so
> that if you ever want to change the slots of a class, you can leave the old
> accessors (with the proper translations) and change the slots, and nothing
> breaks. I tend to agree with that. Similarly, Microsoft recommends not
> making public fields (slots) in your class, but only properties (accessors)
> wrapping the fields.
> 

Here's altogether another perspective:
http://www.javaworld.com/javaworld/jw-09-2003/jw-0905-toolbox_p.html

David Sletten
From: Pascal Bourguignon
Subject: Re: Slot accessors and syntax
Date: 
Message-ID: <87r7lsmzh4.fsf@thalassa.informatimago.com>
Chris Capel <······@iba.nktech.net> writes:
>    Declaring accessors in my defclass,
> 3)   (foo.bar foo-inst), (foo.baz foo-inst)

What about this syntax:

(PJB-DEFCLASS name (super1 super2)
    (:ATT name type [ init-value [doc-string] | doc-string ] )
    (:ATT name type [ init-value [doc-string] | doc-string ] )
    (:ATT name type [ init-value [doc-string] | doc-string ] ))

which generates the accessors automatically.  (You could change it to
optionalize the type too, and remove :ATT)


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 7 - OBJECTS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


(DEFMACRO PJB-ATTRIB (NAME TYPE &REST ARGS)
  "
This macro outputs an attribute s-exp as used in defclass.
ARGS  may be of length 1 or 2.
      If (LENGTH ARGS) = 1 
      then if the argument is a string, 
           then it's taken as the documentation and the initial value is NIL
           else it's taken as the initial value and the documentation is NIL.
      else the first is the initial value and the second is the documentation.
The initarg an accessor are the same keyword built from the name.
"
  (LET ((IARG (INTERN (IF (SYMBOLP NAME) (SYMBOL-NAME NAME) NAME)
                          (FIND-PACKAGE "KEYWORD")))
        INIT DOC)
       (COND 
        ((= 2 (LENGTH ARGS))
         (SETQ INIT (CAR  ARGS)
               DOC  (CADR ARGS)) )
        ((= 1 (LENGTH ARGS))
         (IF (STRINGP (CAR ARGS))
             (SETQ INIT NIL
                   DOC  (CAR ARGS))
             (SETQ INIT (CAR ARGS)
                   DOC  NIL)) )
        (T (ERROR "Invalid arguments to PJB-ATTRIB.")))
       (IF (AND (SYMBOLP TYPE) (NULL INIT))
           (SETQ TYPE (LIST 'OR 'NULL TYPE)))
       (IF (NULL DOC)
           (SETQ DOC (SYMBOL-NAME NAME)))
       `(,NAME 
         :INITFORM ,INIT 
         :INITARG  ,IARG
         :ACCESSOR ,NAME
         :TYPE     ,TYPE
         :DOCUMENTATION ,DOC)));;PJB-ATTRIB

  
(DEFMACRO PJB-DEFCLASS (NAME SUPER &REST ARGS)
  "
This macro encapsulate DEFCLASS and allow the declaration of the attributes
in a shorter syntax.
ARGS  is a list of s-expr, whose car is either :ATT (to declare an attribute)
      or :DOC to give the documentation string of the class.
      (:OPT is not implemented yet).
      See PJB-ATTRIB for the syntax of the attribute declation.
      (:ATT name type [ init-value [doc-string] | doc-string ] )
"
  (LET ((FIELDS  NIL)
        (OPTIONS NIL))
       (DO () ( (NOT ARGS) )
           (COND ((EQ :ATT (CAAR ARGS))
                  (PUSH (MACROEXPAND (CONS 'PJB-ATTRIB (CDAR ARGS))) FIELDS))
                 ((EQ :DOC (CAAR ARGS))
                  (PUSH (CONS :DOCUMENTATION (CDAR ARGS)) OPTIONS))
                 )
           (SETQ ARGS (CDR ARGS)))
       (SETQ FIELDS (NREVERSE FIELDS))
       (SETQ OPTIONS (NREVERSE OPTIONS))
       `(DEFCLASS ,NAME ,SUPER ,FIELDS ,@OPTIONS)));;PJB-DEFCLASS

 

-- 
__Pascal Bourguignon__                     http://www.informatimago.com/
Cats meow out of angst
"Thumbs! If only we had thumbs!
We could break so much!"
From: Wade Humeniuk
Subject: Re: Slot accessors and syntax
Date: 
Message-ID: <jMIvd.27582$eb3.8793@clgrps13>
Chris Capel wrote:
> 
> Has anyone taken a difference approach to this, perhaps?
> 

Yes I have.  I do not take it to heart.  Water off a duck's back.
Programming is tedious, pedantic, exacting and OO techniques have
just made it more so.  So I just "Do It!".  The savings in
programming effort are not made in trying to change the nuts and
bolts of CL.  The real benfits come when one creates a more
descriptive language to program the solution.  Peter Norvig's
"Paradigms of Artificial Intelligence Programming"
http://www.norvig.com/paip.html has great examples of doing this.
The grammer syntax http://www.norvig.com/paip/syntax1.lisp
is great example of an alternative.

Perhaps you could post some real code that is causing you
grief instead of these simplified examples.  There is nothing
like actual code to get one thinking of better ways.

Wade
From: Kenny Tilton
Subject: Re: Slot accessors and syntax
Date: 
Message-ID: <XdJvd.24695$Yh2.11201524@twister.nyc.rr.com>
Wade Humeniuk wrote:

> Chris Capel wrote:
> 
>>
>> Has anyone taken a difference approach to this, perhaps?
>>
> 
> Yes I have.  I do not take it to heart.  Water off a duck's back.
> Programming is tedious, pedantic, exacting and OO techniques have
> just made it more so.  So I just "Do It!".

check. when I had the conflict with "window" my window class got renamed 
"windo", which one of my non-native programmers pronounced "win-doo", 
which sounded so good it became the preferred shop vocalization.

kt

-- 
Cells? Cello? Celtik?: http://www.common-lisp.net/project/cells/
Why Lisp? http://alu.cliki.net/RtL%20Highlight%20Film
From: Michael Walter
Subject: Re: Slot accessors and syntax
Date: 
Message-ID: <329touF3hs596U1@individual.net>
Kenny Tilton wrote:
> check. when I had the conflict with "window" my window class got renamed 
> "windo", which one of my non-native programmers pronounced "win-doo", 
> which sounded so good it became the preferred shop vocalization.
LOL! It's nice to be back on c.l.lisp :-)

- Michael
From: Alan Crowe
Subject: Re: Slot accessors and syntax
Date: 
Message-ID: <86is74wmdr.fsf@cawtech.freeserve.co.uk>
Chris Capel wrote:
> But the trouble with declaring accessors in CLOS is that
> they become globally visible functions in the package....
> So it's common practice to either include the class name
> in the symbols of most accessors,..
>
> (bullet.position bullet)

OK, some good names, such as position, are used up by CL, so
lets talk about (bullet.location bullet).

I'm not seeing the problem here. I imagine using CLOS something like
this:

(defclass massive () (weight))

(defclass localised ()
    ((x :accessor x-coord :initarg :x)
     (y :accessor y-coord :initarg :y)))

(defclass moving () (u v))

(defclass bullet (massive localised moving)
    ((calibre :initform 0.303)))

(defmethod location ((thing localised))
    (vector (x-coord thing)
	    (y-coord thing)))

(location (make-instance 'bullet :x 5 :y 7)) => #(5 7)

This kind of inheritance is central to OOP. You want one
dollop of code to do the location stuff for /all/ your
objects, not just your bullets.

So when could (bullet.location bullet) make sense?
Location would need to be a slot that wasn't inherited.
Bullet would need to be a class that would never be
subclassed.

Even so, as Chris says:
> But when you use an accessor, it takes up the function
> slot for that symbol (though since it's a generic function
> you can use it for other generic functions, including
> other accessors).

The generic function thing is HUGE!
You get to continue the source file I've started to sketch with:

(defclass house ()
  ((location :accessor location :initform "1600 Pennsylvania Avenue")))
(defvar white-house (make-instance 'house))

(location white-house) => "1600 Pennsylvania Avenue"

So what is the point of (house.location white-house)?  Why
might I prefer it over occassionally adding declarations or
assertions to comment my source in particular cases in
which I fear confusion? e.g.

(let ((home (make-instance 'house))
      (ammo (make-instance 'bullet :x 5 :y 7)))
    (declare (type house home) 
	     (type bullet ammo))
    (check-type home house)
    (check-type ammo bullet)
    (mapcar (function location) (list home ammo)))

=> ("1600 Pennsylvania Avenue" #(5 7))

Alan Crowe 
Edinburgh
Scotland
From: Svein Ove Aas
Subject: Re: Slot accessors and syntax
Date: 
Message-ID: <cpo7v8$1jt$1@services.kq.no>
begin  quoting Alan Crowe :

> (let ((home (make-instance 'house))
>       (ammo (make-instance 'bullet :x 5 :y 7)))
>     (declare (type house home)
> (type bullet ammo))
>     (check-type home house)
>     (check-type ammo bullet)

These check-type forms are in the wrong place.
If safety's low enough (or speed's high enough) that the declarations have
any effect, then the compiler will realize that "home" can only be an
instance of house anyway, so the check-type always has the same result. (I
mean, you declared it thus...)

What you want is probably to put the check-type forms before the
declaration. Also, you should know that most optimizers will realize that
you only assign to home once, in a way that guarantees it's a house anyway.
(Unless, of course, that isn't actually true, in which case the declaration
could have a point.)
From: Alan Crowe
Subject: Re: Slot accessors and syntax
Date: 
Message-ID: <86fz27x2vz.fsf@cawtech.freeserve.co.uk>
Svein Ove Aas wrote:
> Also, you should know that most optimizers will realize
> that you only assign to home once, in a way that
> guarantees it's a house anyway.  (Unless, of course, that
> isn't actually true, in which case the declaration could
> have a point.)

I actually had something rather different in mind.
One could understand the attraction of

(house-location white-house)

over

(location white-house)

by thinking of the class prefix in the accessor name as
being a kind of comment, ie

(house-location white-house) is an alternative to

(location white-house) ;accessing house class

or 

(location white-house) ;white-house is of type house

or

(check-type white-house house)
(location white-house)

Of these four possibilities I much prefer the last.
One might wish to call (check-type white-house house) a
comment, because it is aimed at the human reader of the
source, but is has a great advantage over an actual
comment such as ";white-house is of type house". The reader
can trust the check-type; an actual comment might be
out of date.

I feel that I haven't really understood Chris's original
concern. It did seem to include migrating information that
could be in comments into the source itself, by using a
longer and more description name. There are other ways of
migrating information from comments to source.

Alan Crowe
Edinburgh
Scotland
From: Adam Warner
Subject: Re: Slot accessors and syntax
Date: 
Message-ID: <pan.2004.12.14.11.16.59.155373@consulting.net.nz>
Hi Chris Capel,

> I think I'm willing to forego the "." as used for constructing dotted lists
> (not exactly something I do all the time),

(Consider the ramifications with third party source)

> and even deal with the annoyance of using libraries that have dots in
> their exported symbols, if I were able to give it some sort of reader
> magic that lets me type, e.g., "foo-inst.baz" and have it read in as
> "(baz foo-inst)". Or maybe a different character would be better.
> (Suggestions?) But I don't think it's actually /possible/ to write such
> a magic macro.

It's not possible.

> Can a read macro reclaim the previous symbol in the stream and replace
> it with something else?

No.

How about this notation:
[foo-inst baz] => (baz foo-inst)

You could even consider translating arguments:
[a(b c) e() f(g)]  => (f (e (a b c)) g)

It's smart to leave much of the reader alone. In this case you are only
defining a dispatching macro character on [ (and a terminating on ]).
You can still read the symbols as normal. The above would be read as:

(a (b c) e nil f (g))

by the macro character, which you would turn into a traditional
s-expression.

Just an idea. In the end you are likely to abandon most custom syntax.

Regards,
Adam
From: Chris Capel
Subject: Re: Slot accessors and syntax
Date: 
Message-ID: <10rtkmu71l3d22d@corp.supernews.com>
Adam Warner wrote:

> Hi Chris Capel,
> 
>> I think I'm willing to forego the "." as used for constructing dotted
>> lists (not exactly something I do all the time),
> 
> (Consider the ramifications with third party source)

Eh? The special syntax would only be enabled while in my own packages. You
can export and import symbols with special read macros and it does The
Right Thing. I've tested that out.

>> But I don't think it's actually /possible/ to write such
>> a magic macro.
> 
> It's not possible.

Blah.

You were supposed to prove me wrong.

> How about this notation:
> [foo-inst baz] => (baz foo-inst)

That doesn't save me any typing. Also, it shares a disadvantage with "(baz
foo-inst)" syntax in that it's partitioned off from the surrounding code by
matching punctuation. That makes it look radically different from the
languages I come from, and it's very uncomfortable. More uncomfortable than
the switched word order: that's just the icing on the cake--the easy part
of baking the cake, a bit more flashy, but not as good as the bread.

I doubt I'll ever get used to "(baz foo-inst)". And this is coming from
someone that *never* disliked Lisp syntax in general, not even when I was
first becoming familiar with the language. Not even on my first glimpse of
Lisp code.

> You could even consider translating arguments:
> [a(b c) e() f(g)]  => (f (e (a b c)) g)

Function calling syntax doesn't generally bug me. That example looks horrid
to my eyes. (I'm not even sure what you're doing.) I'm only concerned with
class member access. Perhaps, more generally, operations that could be
conceptually considered as "dereferencing". Perhaps not.

> It's smart to leave much of the reader alone.

Ugh. *waves hand*

Also, as I said above, using files with really weird read syntax doesn't
really affect anything but those files if you do it right. The only impact
is on maintainability of the actual code being written in the custom
syntax. Hopefully that's a positive impact.

> Just an idea. In the end you are likely to abandon most custom syntax.

I think I like the "$" better than no macros at all. If I do end up
abandoning it, it'd only because the Lisp reader isn't flexible enough.

I suppose I could always implement my own reader. Figure out how to hook it
into SLIME and ASDF. It'd probably need more hooks elsewhere, too. I did a
simple Lisp reader in C# once. Pretty easy.

Nah.

I'll just wait for Arc. I heard Paul's really big on brevity. (Not that I'm
giving up on CL in the meanwhile.)

Chris Capel
From: Fred Gilham
Subject: Re: Slot accessors and syntax
Date: 
Message-ID: <u7fz28ubmb.fsf@snapdragon.csl.sri.com>
> I'll just wait for Arc. I heard Paul's really big on brevity. (Not
> that I'm giving up on CL in the meanwhile.)

Have you looked at Dylan?  I just heard yesterday that Guy Steele said
Sun should have just called Dylan "Java 2" and they would have been
better off.  (I don't know if he really said this.)

Given the direction of your efforts as described in this thread, I'd
be curious to know if you found the Dylan syntax more congenial.

OTOH I don't know how brevity-friendly the Dylan syntax is.

(Just in case there's any doubt, this is meant in all seriousness and
collegiality!)

-- 
Fred Gilham                                       ······@csl.sri.com
The great Irish satirist Jonathan Swift once exposed an astrologer by
prophesying his death on a certain date. When the date passed and the
astrologer protested that he was still alive, Swift, refusing to take
his word for it, puckishly accused his survivors of perpetrating a
hoax.                                              --- Joseph Sobran
From: Edi Weitz
Subject: Re: Slot accessors and syntax
Date: 
Message-ID: <upt1cdel2.fsf@agharta.de>
On 14 Dec 2004 07:45:48 -0800, Fred Gilham <······@snapdragon.csl.sri.com> wrote:

> Have you looked at Dylan?  I just heard yesterday that Guy Steele
> said Sun should have just called Dylan "Java 2" and they would have
> been better off.  (I don't know if he really said this.)

Hehe, that's cool.  If you can confirm this quote, please post it
here.

Thanks,
Edi.

-- 

Lisp is not dead, it just smells funny.

Real email: (replace (subseq ·········@agharta.de" 5) "edi")
From: John Thingstad
Subject: Re: Slot accessors and syntax
Date: 
Message-ID: <opsi0bg404pqzri1@mjolner.upc.no>
On 14 Dec 2004 07:45:48 -0800, Fred Gilham <······@snapdragon.csl.sri.com>  
wrote:

>
>> I'll just wait for Arc. I heard Paul's really big on brevity. (Not
>> that I'm giving up on CL in the meanwhile.)
>
> Have you looked at Dylan?  I just heard yesterday that Guy Steele said
> Sun should have just called Dylan "Java 2" and they would have been
> better off.  (I don't know if he really said this.)
>
> Given the direction of your efforts as described in this thread, I'd
> be curious to know if you found the Dylan syntax more congenial.
>
> OTOH I don't know how brevity-friendly the Dylan syntax is.
>
> (Just in case there's any doubt, this is meant in all seriousness and
> collegiality!)
>

I found the infix syntax to be of little relevance
except when writing macroes. This becomes much
more difficult.

-- 
Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
From: Marcin 'Qrczak' Kowalczyk
Subject: Re: Slot accessors and syntax
Date: 
Message-ID: <87llc01yqp.fsf@qrnik.zagroda>
Fred Gilham <······@snapdragon.csl.sri.com> writes:

>> I'll just wait for Arc. I heard Paul's really big on brevity.
>> (Not that I'm giving up on CL in the meanwhile.)
>
> Have you looked at Dylan?

Dylan is quite verbose, e.g. "define method blahblah (params) ... end;"
and customary (even if optional) type annotations.

-- 
   __("<         Marcin Kowalczyk
   \__/       ······@knm.org.pl
    ^^     http://qrnik.knm.org.pl/~qrczak/
From: Chris Capel
Subject: Re: Slot accessors and syntax
Date: 
Message-ID: <10rv46cp55ds19d@corp.supernews.com>
Marcin 'Qrczak' Kowalczyk wrote:

> Fred Gilham <······@snapdragon.csl.sri.com> writes:
> 
>>> I'll just wait for Arc. I heard Paul's really big on brevity.
>>> (Not that I'm giving up on CL in the meanwhile.)
>>
>> Have you looked at Dylan?
> 
> Dylan is quite verbose, e.g. "define method blahblah (params) ... end;"
> and customary (even if optional) type annotations.

Do you have a better example? "define method foo (a b c) ... end;" doesn't
seem all that bad to me, except for the weird semicolon at the end.

In fact, you could abbreviate "define method" to "defmethod". You could add
a "(" at the beginning and replace "end;" with ")". You've gained a few
characters. But I wouldn't call that a difference in consicion.

Chris Capel
From: Marcin 'Qrczak' Kowalczyk
Subject: Re: Slot accessors and syntax
Date: 
Message-ID: <87k6rke0fu.fsf@qrnik.zagroda>
Chris Capel <······@iba.nktech.net> writes:

>> Dylan is quite verbose, e.g. "define method blahblah (params) ... end;"
>> and customary (even if optional) type annotations.
>
> Do you have a better example? "define method foo (a b c) ... end;" doesn't
> seem all that bad to me, except for the weird semicolon at the end.

There are several small factors which, when taken together, lead to
verbose code:

- function invocation is "f (x, y, z)", compared to Haskell and ML
  "f x y z", or even Lisp "(f x y z)"
- syntax uses "end" instead of braces
- many people write "end method foo", "end if" etc. even though the
  part after "end" is optional
- many people write types of function parameters and results even in
  case the generic function has only a single method
- a naming convention for types uses two extra characters: <foo>
- words in names are separated with hyphens rather than case changes
- punctuation is allowed in identifiers, and thus you must always
  write i + 1 instead of i+1
- some keywords and builtin macros are long, e.g. "otherwise",
  "define constant".

An example from Dylan manual (which demonstrates closures):

define method make-score (points :: <number>)
  method (increase :: <number>)
    points := points + increase;
  end method;
end method make-score;
define constant score-david = make-score(100)
[...]

This could be shortened:

define method make-score (points)
  method (increase)
    points := points + increase;
  end;
end;
define constant score-david = make-score(100)

but in Ruby it's even shorter:

def makeScore(points)
  proc {|increase| points += increase}
end
scoreDavid = makeScore(100)

(You could remove all parens here but it would not be idiomatic.)

Note that a syntax with "end" encourages to always split the body into
lines, while a syntax with braces encourages to put a short body on
the same line.

> In fact, you could abbreviate "define method" to "defmethod".

I think not:

http://gauss.gwydiondylan.org/books/drm/drm_15.html

"A user-defined defining macro is a macro that defines a definition
in terms of other constructs. A call to a user-defined defining macro
always begins with the word define and includes the name of the
defining macro."

-- 
   __("<         Marcin Kowalczyk
   \__/       ······@knm.org.pl
    ^^     http://qrnik.knm.org.pl/~qrczak/