From: Johann Hibschman
Subject: my problems with CLOS
Date: 
Message-ID: <mtlmu7i329.fsf@astron.berkeley.edu>
The title is provocative, sure, but I'd honestly like some feedback.

I'm not a lisp hacker with many years of experience under my belt.
I've done a project or two in CL, but there are many gaps in my
knowledge.  I know this; if anything I mention is easily solved, just
let me know.

That being said, I find myself more cognitively comfortable with
object systems which effectively give objects their own namespaces.
Things such as "obj.method" only define "method" within the class of
obj, for example.  Within CLOS, there would only be one thing called
"method" that I can manipulate.

For example, consider an object with a method "print."  First off,
it's already defined as a function, which leaves me uncomfortable.
Even stranger, StringClass.print(output-file) and Toddler.print(text,
output-file) could not both be defined in CLOS, since they require
different numbers of arguments.

How can I get around this?

The most obvious way, that I've found, is to simply make a separate
pacakge, representing the "interface" implemented by a generic
function.  StringClass.print would be something like (file:print
(s StringClass) (f stream)), while Toddler.print would be something like
(writing:print (p Toddler) (text string) (f stream)).

This seems to require prefacing every instance of the method with the
interface package name, which seems quite cumbersome.

Is there a clever way around this?  Or is it a feature? :-)

-- 
Johann Hibschman                           ······@physics.berkeley.edu

From: Kent M Pitman
Subject: Re: my problems with CLOS
Date: 
Message-ID: <sfw3dgfnnhy.fsf@world.std.com>
Johann Hibschman <······@physics.berkeley.edu> writes:

> That being said, I find myself more cognitively comfortable with
> object systems which effectively give objects their own namespaces.

Lisp specifically tries not to do this.  You're welcome, of course, to
shadow all the Lisp functionality and build up alternate functionality
you like better.

> Things such as "obj.method" only define "method" within the class of
> obj, for example.  Within CLOS, there would only be one thing called
> "method" that I can manipulate.

Use packages for this kind of partitioning.

> For example, consider an object with a method "print."  First off,
> it's already defined as a function, which leaves me uncomfortable.
> Even stranger, StringClass.print(output-file) and Toddler.print(text,
> output-file) could not both be defined in CLOS, since they require
> different numbers of arguments.
> 
> How can I get around this?

Lisp used to be like this and it was found that these were huge sources of
program error.  The languages such as you describe tend to be statically
compiled ones with a closed-world kind of model, often based on single
inheritance.

In most cases where you have different argument conventions, you have a 
different conceptual function.  In Lisp, we felt you should pick a different
name so as not to confuse people.  It's routine for the same conceptual
function to span multiple classes, so we made generics such that they do this.
But we couldn't make them both span multiple classes and private to a class.
It has to be one way or the other, and the way we did it was most useful.

Generic functions also help in a world where dynamic loading can occur because
they define extensible protocols that other objects might want to adhere to.
It's tempting to make assumptions about the "lack of conflict" you perceive
in doing the overloading, but in Lisp you can add code later, so making 
assumptions about what code does and doesn't do is often nearsighted.  In Java,
the whole language is nearsighted because of the static assumptions it makes
over and over, so most people don't mind the extra nearsightedness the language
feature you're talking about causes.

> The most obvious way, that I've found, is to simply make a separate
> pacakge, representing the "interface" implemented by a generic
> function.  StringClass.print would be something like (file:print
> (s StringClass) (f stream)), while Toddler.print would be something like
> (writing:print (p Toddler) (text string) (f stream)).

Yes, that's the right way to do it.

> This seems to require prefacing every instance of the method with the
> interface package name, which seems quite cumbersome.
 
Well, no.  It requires for any given package picking one of the two which
will have a short name (the unprefixed one) and referencing only the other
one as a long name.

You can also make a nickname if it gets called a lot by normal functional
definition. e.g.,

 (declaim (inline fu))
 (defun fu (x) (frobozz:foo x))

and then use fu where frobozz:foo would have been needed (except in method
definitions, which still have to work on frobozz:foo, not fu).

> Is there a clever way around this?  Or is it a feature? :-)

It is definitely a feature.

What you are describing is a nightmare we lived for nearly ten years in
Lisp Machine flavors, and finally got rid of.  Old-style Lisp Machine
send was exactly as you describe, allowing operator overloading, and it
made it very hard in a non-statically-typed system for commands like
"show arglist" in the editor to work right because (send x :print)
didn't have any obvious way of knowing what the value of x was, and :print
might have different args depending on the type.  It also meant that when
a single function inherited a :print from two unrelated classes through
multiple inheritance, one was shadowed even if it did something unrelated
such that both should still show through.

Really if you think about what Java and C++ think of as a class as 
corresponding more to a package (or at least a package+class), you'll be
closer to what you want.  It is wrong to assume that Lisp seeks to solve
the same set of problems as other languages do through classes just because
of the "false cognate" effect of both languages having called them "classes".
Lisp classes simply do not partition namespaces, and quite intentionally so.
It is a feature, not a bug, that  you can do

 (defclass foo:foo () ...)
 (defmethod foo:frob ((x foo:foo)) ...)

 (defclass bar:xyz () ...)
 (defclass bar:frob ((x bar:xyz) (y bar:xyz)) ...)

 (defclass baz:fooxyz (foo:foo bar:xyz) ...)

and have both foo:frob and bar:frob available without conflict on objects
of class baz:fooxyz.

This kind of thing doesn't happen in most other class systems, where class
and namespace are the same thing.  Or, to the extent that it does, it's done
by casting that doesn't look really a lot different than package prefixing.
From: Johann Hibschman
Subject: Re: my problems with CLOS
Date: 
Message-ID: <mtr93ygebu.fsf@astron.berkeley.edu>
Kent M Pitman writes:

> Johann Hibschman <······@physics.berkeley.edu> writes:

> Really if you think about what Java and C++ think of as a class as 
> corresponding more to a package (or at least a package+class), you'll be
> closer to what you want.  It is wrong to assume that Lisp seeks to solve
> the same set of problems as other languages do through classes just because
> of the "false cognate" effect of both languages having called them "classes".
> Lisp classes simply do not partition namespaces, and quite intentionally so.

Thanks for the information.  I'll have to think about it more, as
usual.  One of my favorite things about CL is that almost everything
is the product of more thought than it seems.

I can see where packages would map more to my previous experience with
classes than CLOS classes, and the rest are clearly defensible points.
I lack the experience to know if they're true, but I'll try to keep it
in mind as I go.

-- 
Johann Hibschman                           ······@physics.berkeley.edu
From: Andrew K. Wolven
Subject: Re: my problems with CLOS
Date: 
Message-ID: <3A253A98.D1A77270@redfernlane.org>
Thanks, Kent.
You answered a question I had.

Kent M Pitman wrote:

> Johann Hibschman <······@physics.berkeley.edu> writes:
>
> > That being said, I find myself more cognitively comfortable with
> > object systems which effectively give objects their own namespaces.
>
> Lisp specifically tries not to do this.  You're welcome, of course, to
> shadow all the Lisp functionality and build up alternate functionality
> you like better.
>
> > Things such as "obj.method" only define "method" within the class of
> > obj, for example.  Within CLOS, there would only be one thing called
> > "method" that I can manipulate.
>
> Use packages for this kind of partitioning.
>
> > For example, consider an object with a method "print."  First off,
> > it's already defined as a function, which leaves me uncomfortable.
> > Even stranger, StringClass.print(output-file) and Toddler.print(text,
> > output-file) could not both be defined in CLOS, since they require
> > different numbers of arguments.
> >
> > How can I get around this?
>
> Lisp used to be like this and it was found that these were huge sources of
> program error.  The languages such as you describe tend to be statically
> compiled ones with a closed-world kind of model, often based on single
> inheritance.
>
> In most cases where you have different argument conventions, you have a
> different conceptual function.  In Lisp, we felt you should pick a different
> name so as not to confuse people.  It's routine for the same conceptual
> function to span multiple classes, so we made generics such that they do this.
> But we couldn't make them both span multiple classes and private to a class.
> It has to be one way or the other, and the way we did it was most useful.
>
> Generic functions also help in a world where dynamic loading can occur because
> they define extensible protocols that other objects might want to adhere to.
> It's tempting to make assumptions about the "lack of conflict" you perceive
> in doing the overloading, but in Lisp you can add code later, so making
> assumptions about what code does and doesn't do is often nearsighted.  In Java,
> the whole language is nearsighted because of the static assumptions it makes
> over and over, so most people don't mind the extra nearsightedness the language
> feature you're talking about causes.
>
> > The most obvious way, that I've found, is to simply make a separate
> > pacakge, representing the "interface" implemented by a generic
> > function.  StringClass.print would be something like (file:print
> > (s StringClass) (f stream)), while Toddler.print would be something like
> > (writing:print (p Toddler) (text string) (f stream)).
>
> Yes, that's the right way to do it.
>
> > This seems to require prefacing every instance of the method with the
> > interface package name, which seems quite cumbersome.
>
> Well, no.  It requires for any given package picking one of the two which
> will have a short name (the unprefixed one) and referencing only the other
> one as a long name.
>
> You can also make a nickname if it gets called a lot by normal functional
> definition. e.g.,
>
>  (declaim (inline fu))
>  (defun fu (x) (frobozz:foo x))
>
> and then use fu where frobozz:foo would have been needed (except in method
> definitions, which still have to work on frobozz:foo, not fu).
>
> > Is there a clever way around this?  Or is it a feature? :-)
>
> It is definitely a feature.
>
> What you are describing is a nightmare we lived for nearly ten years in
> Lisp Machine flavors, and finally got rid of.  Old-style Lisp Machine
> send was exactly as you describe, allowing operator overloading, and it
> made it very hard in a non-statically-typed system for commands like
> "show arglist" in the editor to work right because (send x :print)
> didn't have any obvious way of knowing what the value of x was, and :print
> might have different args depending on the type.  It also meant that when
> a single function inherited a :print from two unrelated classes through
> multiple inheritance, one was shadowed even if it did something unrelated
> such that both should still show through.
>
> Really if you think about what Java and C++ think of as a class as
> corresponding more to a package (or at least a package+class), you'll be
> closer to what you want.  It is wrong to assume that Lisp seeks to solve
> the same set of problems as other languages do through classes just because
> of the "false cognate" effect of both languages having called them "classes".
> Lisp classes simply do not partition namespaces, and quite intentionally so.
> It is a feature, not a bug, that  you can do
>
>  (defclass foo:foo () ...)
>  (defmethod foo:frob ((x foo:foo)) ...)
>
>  (defclass bar:xyz () ...)
>  (defclass bar:frob ((x bar:xyz) (y bar:xyz)) ...)
>
>  (defclass baz:fooxyz (foo:foo bar:xyz) ...)
>
> and have both foo:frob and bar:frob available without conflict on objects
> of class baz:fooxyz.
>
> This kind of thing doesn't happen in most other class systems, where class
> and namespace are the same thing.  Or, to the extent that it does, it's done
> by casting that doesn't look really a lot different than package prefixing.
From: David J. Cooper Jr.
Subject: Re: my problems with CLOS
Date: 
Message-ID: <pmqn1em220m.fsf@mie.genworks.com>
Johann Hibschman <······@physics.berkeley.edu> writes:
>
> Even stranger, StringClass.print(output-file) and Toddler.print(text,
> output-file) could not both be defined in CLOS, since they require
> different numbers of arguments.
>
> How can I get around this?
> 

In addition to what Kent said, another approach might be:

 (defmethod print-object ((self string)(output-file pathname) &rest args)
   ...) ;; args should be NIL when this is called.

 (defmethod print-object ((self toddler)(output-file pathname) &rest args)
   ...) ;; (FIRST ARGS) should be your text string when this is called.

Of course this kind of thing prevents the complete use of ``show
arglist,'' the same ``nightmare'' issue which Kent mentions existed
with Lisp Machine Flavors. A bit ugly but in some cases this kind of
thing could be appropriate.

 -dave

-- 
David J. Cooper Jr, Chief Engineer	Genworks International
·······@genworks.com                    5777 West Maple, Suite 130
(248) 932-2512 (Genworks HQ/voicemail)	West Bloomfield, MI 48322-2268
(248) 407-0633 (pager)			http://www.genworks.com
From: Thomas A. Russ
Subject: Re: my problems with CLOS
Date: 
Message-ID: <ymig0kdz37z.fsf@sevak.isi.edu>
·······@genworks.com (David J. Cooper Jr.) writes:

> 
> 
> Johann Hibschman <······@physics.berkeley.edu> writes:
> >
> > Even stranger, StringClass.print(output-file) and Toddler.print(text,
> > output-file) could not both be defined in CLOS, since they require
> > different numbers of arguments.
> >
> > How can I get around this?
> > 
> 
> In addition to what Kent said, another approach might be:
> 
>  (defmethod print-object ((self string)(output-file pathname) &rest args)
>    ...) ;; args should be NIL when this is called.
> 
>  (defmethod print-object ((self toddler)(output-file pathname) &rest args)
>    ...) ;; (FIRST ARGS) should be your text string when this is called.
> 
> Of course this kind of thing prevents the complete use of ``show
> arglist,'' the same ``nightmare'' issue which Kent mentions existed
> with Lisp Machine Flavors. A bit ugly but in some cases this kind of
> thing could be appropriate.

There is a potentially insidious problem with this class of solutions.

One of the features that generic functions and object-oriented
programming is supposed to provide is the substitutability of objects in
method calls.  

In other words, if you end up needing to use special rules about which
arguments (keyword or optional or rest) need to be specified depending
on the type of one of the dispatch arguments, then it seems you have
given up a large part of the advantage that OO programming is supposed
to provide to you:  Namely that methods are conceptually the same
operation, and that the caller of the method shouldn't have to care
about the specific type of the argument in order to write a correct
function call.  If there are additional elements to the calling protocol
that violate this design guideline, then the genericity of the code is
reduced, which makes maintenance, debugging and reuse of the code more
difficult.


-- 
Thomas A. Russ,  USC/Information Sciences Institute          ···@isi.edu    
From: Marco Antoniotti
Subject: Re: my problems with CLOS
Date: 
Message-ID: <y6cvgtaee14.fsf@octagon.mrl.nyu.edu>
Johann Hibschman <······@physics.berkeley.edu> writes:

> The title is provocative, sure, but I'd honestly like some feedback.
> 
> I'm not a lisp hacker with many years of experience under my belt.
> I've done a project or two in CL, but there are many gaps in my
> knowledge.  I know this; if anything I mention is easily solved, just
> let me know.
> 
> That being said, I find myself more cognitively comfortable with
> object systems which effectively give objects their own namespaces.
> Things such as "obj.method" only define "method" within the class of
> obj, for example.  Within CLOS, there would only be one thing called
> "method" that I can manipulate.
> 
> For example, consider an object with a method "print."  First off,
> it's already defined as a function, which leaves me uncomfortable.
> Even stranger, StringClass.print(output-file) and Toddler.print(text,
> output-file) could not both be defined in CLOS, since they require
> different numbers of arguments.
> 
> How can I get around this?
> 
> The most obvious way, that I've found, is to simply make a separate
> pacakge, representing the "interface" implemented by a generic
> function.  StringClass.print would be something like (file:print
> (s StringClass) (f stream)), while Toddler.print would be something like
> (writing:print (p Toddler) (text string) (f stream)).

At least n this respect, CL will force you to do things a little
differently (and IMHO more cleanly).

First of all, you should ask why you need different argument lists.
Sometimes this is not needed and sometimes it would be better to have
different names.  Let's consider a "more standard" case: drawing a
line on a "window".  In a Java-like world you'd have something like

	class Point { int x, y; ... }
	class Window {
		void draw(Point p1, Point p2) { ... }
		void draw(int x1, int y1, int x2, int y2) { ... }
	}

In CL, you ca use a different convetion (which I first saw in CLX and
CLIM and which I like) that looks like

	(defclass point () ...)
	(defclass window () ...)

	(defgeneric draw (w p1 p2))
	(defgeneric draw* (w x1 y1 x2 y2))
		
I.e. you use the convetion of making a "standard" function and one
that takes "spread" arguments.  With this trick you already take care
of a good chunk of the overloading cases.

Another set of "overloading" uses in Java and C++ like languages is
the "multiple constructor setup" for a given class.
Most of these cases can be dealt with by setting up a single entry
point

	(defun make-<class> (&rest args &key ....))

If you analyze many of the "standard" Java constructors you will see that
they can be easily recast in this way.

> This seems to require prefacing every instance of the method with the
> interface package name, which seems quite cumbersome.
> 
> Is there a clever way around this?  Or is it a feature? :-)

Yes there is.  Take the MATLISP system for example.

The authors took the decision *not* to "redefine" +, -, etc etc (C++
overloading).  Instead they provide things like M+, M- etc etc.

This is fine, but just consider how easy it is to now provide a
specialized package like the following

	(defpackage "MATLISP-USER-2" (:use "COMMON-LISP MATLISP")
	   (:nicknames "MU2" "mu2" "matlisp-user-2")  ; guess why :)
           (:shadow cl:+ cl:- cl:/ cl:= cl:* ....)
	   )

	(in-package "MU2")

	(defgeneric + (x y)) ; You can get fancier and have the
                             ; multiple args as well.

        (defmethod + ((x number) (y number)) (cl:+ x y))

        (defmethod + ((x fixnum) (y fixnum)) (cl:+ x y)) ; Fancier :)

	(defmethod + ((x real-matrix) (y real-matrix)) (m+ x y))

	....

Then you would start the system in your MATLISP-USER-2 package and
have all the known abbreviations at hand. (Incidentally you cannot do
that in Java; you can in C++, but I do not know about other languages).

Yes, all in all you need to be more careful. But that is IMHO good.

Cheers

-- 
Marco Antoniotti =============================================================
NYU Bioinformatics Group			 tel. +1 - 212 - 998 3488
719 Broadway 12th Floor                          fax  +1 - 212 - 995 4122
New York, NY 10003, USA				 http://galt.mrl.nyu.edu/valis
             Like DNA, such a language [Lisp] does not go out of style.
			      Paul Graham, ANSI Common Lisp