Discussion:
Generics vs. Message passing: object namespaces
(too old to reply)
Tom Locke
2007-02-22 09:26:32 UTC
Permalink
I may well have missed it, but in all this talk of generic-function
based OO vs. message-passing OO, what about the issue of namespaces
scoped to objects.

e.g.

buffer.append(x) vs. append_to_buffer(buffer, x)

I believe I am correct in stating that the generic function style
requires the latter? Sharing a single generic function "append" among
the very wide range of objects that might provide a definition isn't
workable. Maybe someone will pull me up on this particular example,
but one only has to take a look at any CLOS or Dylan code to see that
the issue is real.

To my mind one of the chief benefits of the OO style is in
organisation of code. We put things in places that makes sense, and
we find them easily later. Yes this breaks down if a method belongs
equally in two places, but it's still a big net win IMO.

In message-passing style OO, it's often possible to simply guess the
method name, and there is often support in the editor for enumerating
the available methods. So writing is easier. Reading is easier too -
that "_to_buffer" in the above example is largely noise. Noise that
adds up pretty quickly.

I wonder if language designers, with their theoretical bent, find
this issue of little importance - to a mathematician, a name is just
a name. As long as it is distinct, what else matters? To us humans,
it matters a lot!

As I said - maybe this issue did already get raised -- maybe in
language too fancy for me to notice ;-) Either way, from a pragmatic
point of view, I'd say this is issue numero uno, so some extra
clarity would be very interesting.

Proponents of CLOS - what say you?

Tom
Pascal Costanza
2007-02-22 10:33:56 UTC
Permalink
Post by Tom Locke
I may well have missed it, but in all this talk of generic-function
based OO vs. message-passing OO, what about the issue of namespaces
scoped to objects.
e.g.
buffer.append(x) vs. append_to_buffer(buffer, x)
I believe I am correct in stating that the generic function style
requires the latter? Sharing a single generic function "append"
among the very wide range of objects that might provide a
definition isn't workable. Maybe someone will pull me up on this
particular example, but one only has to take a look at any CLOS or
Dylan code to see that the issue is real.
To my mind one of the chief benefits of the OO style is in
organisation of code. We put things in places that makes sense, and
we find them easily later. Yes this breaks down if a method belongs
equally in two places, but it's still a big net win IMO.
In message-passing style OO, it's often possible to simply guess
the method name, and there is often support in the editor for
enumerating the available methods. So writing is easier. Reading is
easier too - that "_to_buffer" in the above example is largely
noise. Noise that adds up pretty quickly.
I wonder if language designers, with their theoretical bent, find
this issue of little importance - to a mathematician, a name is
just a name. As long as it is distinct, what else matters? To us
humans, it matters a lot!
As I said - maybe this issue did already get raised -- maybe in
language too fancy for me to notice ;-) Either way, from a
pragmatic point of view, I'd say this is issue numero uno, so some
extra clarity would be very interesting.
Proponents of CLOS - what say you?
That's an issue of namespace management, and orthogonal to whether
you use a generic function approach or a message sending approach.

With a good module / package / type system, you can say (append
buffer x) when it's unambiguous what you mean, and (buffer:append
buffer x) when it's not. [Think append(buffer, x) and
ll.discuss.Buffer.append(buffer, x) in more orthodox languages. ;) ]

Many message-sending style OOP approaches provide some form of
namespace management as well, so the issue seems to come up sooner or
later anyway, independent of programming style.

I think it's problematic when the same language construct is
overloaded with many different duties (as a basis for namespace
management, the type system, dynamic dispatch, access control, and so
on). This tends to make each ingredient less flexible than necessary
due to compromises with the not necessarily compatible semantic
issues of the other ingredients.


Pascal
--
Pascal Costanza, mailto:***@p-cos.net, http://p-cos.net
Vrije Universiteit Brussel, Programming Technology Lab
Pleinlaan 2, B-1050 Brussel, Belgium
Tom Locke
2007-02-22 23:14:07 UTC
Permalink
Post by Pascal Costanza
With a good module / package / type system, you can say (append
buffer x) when it's unambiguous what you mean, and (buffer:append
buffer x) when it's not.
Does "good" mean better than anything we currently have in a
production-grade language?

CLOS and Dylan do in fact suffer from type-name-in-the-method-name
syndrome do they not?

This assurance is of little comfort if, in a given (non statically
typed) language, it's always ambiguous.

Tom
Joe Marshall
2007-02-22 23:25:42 UTC
Permalink
Post by Tom Locke
CLOS and Dylan do in fact suffer from type-name-in-the-method-name
syndrome do they not?
Not if they are done correctly.
--
~jrm
Chris Double
2007-02-22 23:37:37 UTC
Permalink
Post by Tom Locke
Does "good" mean better than anything we currently have in a
production-grade language?
CLOS and Dylan do in fact suffer from type-name-in-the-method-name
syndrome do they not?
Dylan has module and library support allowing renaming of imports and
exports. So it's definitely possible to have a 'draw' generic that
does one thing, and a generic with the same name in a different module
that does something different, with different number of arguments,
etc.

If you need to have both 'draw' generics you then rename them on
import when defining the module so they are distinct.

The reason a lot of libraries go for the type-name-in-the-method-name
approach is because using this import/export can be a pain if it's a
commonly used name (like 'add', etc). You can end up with a lot of
renames in the module import.

Chris.
--
http://www.bluishcoder.co.nz
Tom Locke
2007-02-23 09:15:19 UTC
Permalink
Post by Chris Double
If you need to have both 'draw' generics you then rename them on
import when defining the module so they are distinct.
Which is precisely the problem - I can't have picture.draw, gun.draw
and curtains.draw in the same source file, even though, as a human
reader, that would cause me no difficulties whatsoever. Rather, it
makes the code much easier to read.
Post by Chris Double
When I first encountered CLOS, coming from
a standard C++ -> Java background, it seemed downright strange. ...
Having worked with CLOS more now, I really appreciate the CLOS model.
The points you've raised are not in question - the generic function
approach undoubtedly yields a lot of expressive power. I'm just
asking if the price is a loss of nice method names, or is there a
best of both worlds solution?

Joe Marshall
Post by Chris Double
Post by Tom Locke
I believe I am correct in stating that the generic function style
requires the latter? Sharing a single generic function "append" among
the very wide range of objects that might provide a definition isn't
workable.
Why not?
The usual implementation of this sort of thing is a table of
methods... [snip]
I was talking about semantic issues not operational ones. The draw
example above is a better one, where the same name has various
unrelated meanings depending on context. I think differing arity is a
particular problem?
Post by Chris Double
Post by Tom Locke
CLOS and Dylan do in fact suffer from type-name-in-the-method-name
syndrome do they not?
Not if they are done correctly.
Would you like to elaborate?
Post by Chris Double
Post by Tom Locke
Post by Pascal Costanza
With a good module / package / type system, you can say (append
buffer x) when it's unambiguous what you mean, and (buffer:append
buffer x) when it's not.
Does "good" mean better than anything we currently have in a
production-grade language?
CLOS and Dylan do in fact suffer from type-name-in-the-method-name
syndrome do they not?
The simplest answer I can think of is this: Generic functions are
just functions, so any approach that solves namespace management
issues well for function names is automatically also good enough
for generic function names.
The simplest answer, it seems, is not always the clearest! So far no
one has disputed that real-world Dylan and CLOS do actually have this
problem. Why is your "good enough" solution absent?

Tom
Pascal Costanza
2007-02-23 10:07:27 UTC
Permalink
Post by Tom Locke
Post by Chris Double
If you need to have both 'draw' generics you then rename them on
import when defining the module so they are distinct.
Which is precisely the problem - I can't have picture.draw,
gun.draw and curtains.draw in the same source file, even though, as
a human reader, that would cause me no difficulties whatsoever.
Rather, it makes the code much easier to read.
...but for example with the Common Lisp package system, you can have
(paintings:draw picture), (weapons:draw gun) and (decoration:draw
curtain). When you're in a package that :uses, say, weapons (for
example, it's clear that it doesn't use any of the other mentioned
packages), you can just say (draw gun).
Post by Tom Locke
Post by Chris Double
Post by Tom Locke
Post by Pascal Costanza
With a good module / package / type system, you can say (append
buffer x) when it's unambiguous what you mean, and
(buffer:append buffer x) when it's not.
Does "good" mean better than anything we currently have in a
production-grade language?
CLOS and Dylan do in fact suffer from type-name-in-the-method-
name syndrome do they not?
The simplest answer I can think of is this: Generic functions are
just functions, so any approach that solves namespace management
issues well for function names is automatically also good enough
for generic function names.
The simplest answer, it seems, is not always the clearest! So far
no one has disputed that real-world Dylan and CLOS do actually have
this problem. Why is your "good enough" solution absent?
The problem is not harder than elsewhere. If you see large libraries
for message-sending-based approaches, there is also a tendency to get
more "verbose", so it doesn't seem to have anything to do with
generic functions vs. message sending.

I don't mention a specific solution because I consider this an
orthogonal issue. I could use this opportunity to praise the Common
Lisp package system, which in my opinion solves all naming problems
with a single mechanism in an excellent way. However, since I know
only two or three people who agree with me that that's the case ;),
this would just branch off to an otherwise unrelated subthread about
proper namespace management approaches. I'd like to avoid that here.

So I'll stick to my original statement: Whatever mechanism solves
naming issues well for you, will also solve naming issues for generic
functions.

(Just to name two other examples: Dylan has a module system that is
different from Common Lisp's package system, but was designed
alongside Dylan's object system based on generic functions, so they
probably have a good solution here. There exist a number of module
systems for Scheme, and I would be surprised if they didn't mesh well
with generic functions either.)


Pascal
--
Pascal Costanza, mailto:***@p-cos.net, http://p-cos.net
Vrije Universiteit Brussel, Programming Technology Lab
Pleinlaan 2, B-1050 Brussel, Belgium
Dave Roberts
2007-02-26 03:20:38 UTC
Permalink
Post by Tom Locke
Post by Dave Roberts
When I first encountered CLOS, coming from
a standard C++ -> Java background, it seemed downright strange. ...
Having worked with CLOS more now, I really appreciate the CLOS model.
The points you've raised are not in question - the generic function
approach undoubtedly yields a lot of expressive power. I'm just
asking if the price is a loss of nice method names, or is there a
best of both worlds solution?
Is there a price in the loss of nice method names? No, I don't think so.
Whether or not a function is bound tightly to a given object definition,
one can still have the same set of names. A generic function and an
object definition can still be developed in concert, can still live in
the same file. As Pascal has said, they can be disambiguated from each
other with a package name.

In short, I have not found any issues with method naming becoming more
verbose in the case of CLOS. IMO, it really is the best of both worlds
because in the degenerate case it's easy to program with CLOS in a style
that is very similar to a "standard" object model with tightly bound
methods and classes and single dispatch. But CLOS just gives you so much
more if you choose to use it.

-- Dave
Pascal Costanza
2007-02-23 00:17:59 UTC
Permalink
Post by Tom Locke
Post by Pascal Costanza
With a good module / package / type system, you can say (append
buffer x) when it's unambiguous what you mean, and (buffer:append
buffer x) when it's not.
Does "good" mean better than anything we currently have in a
production-grade language?
CLOS and Dylan do in fact suffer from type-name-in-the-method-name
syndrome do they not?
The simplest answer I can think of is this: Generic functions are
just functions, so any approach that solves namespace management
issues well for function names is automatically also good enough for
generic function names.


Pascal
--
Pascal Costanza, mailto:***@p-cos.net, http://p-cos.net
Vrije Universiteit Brussel, Programming Technology Lab
Pleinlaan 2, B-1050 Brussel, Belgium
Randall Randall
2007-02-23 00:47:04 UTC
Permalink
Post by Pascal Costanza
Post by Tom Locke
Post by Pascal Costanza
With a good module / package / type system, you can say (append
buffer x) when it's unambiguous what you mean, and (buffer:append
buffer x) when it's not.
Does "good" mean better than anything we currently have in a
production-grade language?
CLOS and Dylan do in fact suffer from type-name-in-the-method-name
syndrome do they not?
The simplest answer I can think of is this: Generic functions are
just functions, so any approach that solves namespace management
issues well for function names is automatically also good enough
for generic function names.
But these should accomplish different things. Regular functions
need to be kept separate, but generic functions need to be folded
into the same name in the namespace you're using them in, unless
there's some specific reason the programmer doesn't want to do that.


--
Randall Randall <***@randallsquared.com>
"If you are trying to produce a commercial product in
a timely and cost efficient way, it is not good to have
somebody's PhD research on your critical path." -- Chip Morningstar
Pascal Costanza
2007-02-23 09:49:02 UTC
Permalink
Post by Randall Randall
Post by Pascal Costanza
Post by Tom Locke
Post by Pascal Costanza
With a good module / package / type system, you can say (append
buffer x) when it's unambiguous what you mean, and
(buffer:append buffer x) when it's not.
Does "good" mean better than anything we currently have in a
production-grade language?
CLOS and Dylan do in fact suffer from type-name-in-the-method-
name syndrome do they not?
The simplest answer I can think of is this: Generic functions are
just functions, so any approach that solves namespace management
issues well for function names is automatically also good enough
for generic function names.
But these should accomplish different things. Regular functions
need to be kept separate, but generic functions need to be folded
into the same name in the namespace you're using them in, unless
there's some specific reason the programmer doesn't want to do that.
I don't quite follow you. The only difference between functions and
generic functions is that functions are defined in a single place
whereas generic functions can have distributed definitions. This
means that you need access to some namespace when you want to call a
function from that namespace - and this is the same for functions and
generic functions. Additionally, you may need access to some
namespace when you want to define methods on a generic function from
that namespace. But that's the only difference here.

It may be important to stress that methods are essentially anonymous.
Only the generic functions that contain them have names. (In a Scheme-
style language, even the generic functions are anonymous and are
simply bound to variables, so in such languages this all boils down
to having a good modularization mechanism for plain variables.)


Pascal
--
Pascal Costanza, mailto:***@p-cos.net, http://p-cos.net
Vrije Universiteit Brussel, Programming Technology Lab
Pleinlaan 2, B-1050 Brussel, Belgium
--
Pascal Costanza, mailto:***@p-cos.net, http://p-cos.net
Vrije Universiteit Brussel, Programming Technology Lab
Pleinlaan 2, B-1050 Brussel, Belgium
Tom Locke
2007-02-23 10:04:51 UTC
Permalink
(In a Scheme-style language, even the generic functions are
anonymous and are simply bound to variables, so in such languages
this all boils down to having a good modularization mechanism for
plain variables.)
The point about message-dispatch OO, is that the type of the receiver
(or first argument if you prefer) is used to locate the namespace. A
modularization mechanism for plain variables wouldn't get into what
the variable happened to be bound to - the call takes place after the
variable has been looked up, as an entirely separate operation.

You could consider this loss of useful information that has to be
compensated for with more verbose method names.

Tom
Pascal Costanza
2007-02-23 10:15:07 UTC
Permalink
Post by Tom Locke
(In a Scheme-style language, even the generic functions are
anonymous and are simply bound to variables, so in such languages
this all boils down to having a good modularization mechanism for
plain variables.)
The point about message-dispatch OO, is that the type of the
receiver (or first argument if you prefer) is used to locate the
namespace. A modularization mechanism for plain variables wouldn't
get into what the variable happened to be bound to - the call takes
place after the variable has been looked up, as an entirely
separate operation.
That doesn't work well as soon as you incorporate multiple
inheritance (or Smalltalk/Fortress-style traits, for example). Even
without implementation inheritance, pure multiple inheritance of
signatures (like with Java's interfaces) already poses a problem here.

Sooner or later, the libraries of some language will grow bigger, and
then sooner or later, you need objects that adhere to incompatible
interfaces (messages that happen to have the same name but are used
for different purposes). You can then either choose to distribute the
functionality across several objects, and get object identity
problems along the way, or you can choose to incorporate proper
namespace management.


Pascal
--
Pascal Costanza, mailto:***@p-cos.net, http://p-cos.net
Vrije Universiteit Brussel, Programming Technology Lab
Pleinlaan 2, B-1050 Brussel, Belgium
Randall Randall
2007-02-23 16:05:20 UTC
Permalink
Post by Pascal Costanza
Post by Randall Randall
Post by Pascal Costanza
The simplest answer I can think of is this: Generic functions are
just functions, so any approach that solves namespace management
issues well for function names is automatically also good enough
for generic function names.
But these should accomplish different things. Regular functions
need to be kept separate, but generic functions need to be folded
into the same name in the namespace you're using them in, unless
there's some specific reason the programmer doesn't want to do that.
I don't quite follow you. The only difference between functions and
generic functions is that functions are defined in a single place
whereas generic functions can have distributed definitions. This
means that you need access to some namespace when you want to call
a function from that namespace - and this is the same for functions
and generic functions. Additionally, you may need access to some
namespace when you want to define methods on a generic function
from that namespace. But that's the only difference here.
It may be important to stress that methods are essentially
anonymous. Only the generic functions that contain them have names.
(In a Scheme-style language, even the generic functions are
anonymous and are simply bound to variables, so in such languages
this all boils down to having a good modularization mechanism for
plain variables.)
While I understand that the CL package system doesn't
actually do this, I would prefer that weapon:draw and
pen:draw both be accessible as (draw ...) in the package
in which I'm using them. If they have some actual
conflict in the method signatures, then that's a compile
time error, but if they don't, keeping two separate names
seems like it defeats much of the utility of generic
functions -- especially "very" generic ones like length.

So, while normal functions have to be disambiguated with
a package qualifier or explicitly renamed, generic functions
which have methods whose signatures don't conflict are
easily thought of as a single conceptual generic function,
and should be combined in using packages by default. In
my opinion. :)


--
Randall Randall <***@randallsquared.com>
"[W]e ARE the market, this IS the market working, there's nothing
external to be deferred to." -- Ian Bicking, on "let the market
decide"
Pascal Costanza
2007-02-24 00:29:29 UTC
Permalink
Post by Randall Randall
So, while normal functions have to be disambiguated with
a package qualifier or explicitly renamed, generic functions
which have methods whose signatures don't conflict are
easily thought of as a single conceptual generic function,
and should be combined in using packages by default. In
my opinion. :)
I don't want to let this deteriorate into a discussion about the CL
package system, so I will try to keep the response simple: The CL
package system doesn't organize generic functions (or classes or
functions or variables or whatever). It only organizes names. So what
you seem to want doesn't seem possible to me.

I would expect the Dylan module system to be closer to what you want,
but I don't know the details here.



Pascal
--
Pascal Costanza, mailto:***@p-cos.net, http://p-cos.net
Vrije Universiteit Brussel, Programming Technology Lab
Pleinlaan 2, B-1050 Brussel, Belgium
Dave Roberts
2007-02-22 16:13:21 UTC
Permalink
Post by Tom Locke
To my mind one of the chief benefits of the OO style is in
organisation of code. We put things in places that makes sense, and
we find them easily later. Yes this breaks down if a method belongs
equally in two places, but it's still a big net win IMO.
I used to think this as well. When I first encountered CLOS, coming from
a standard C++ -> Java background, it seemed downright strange. I was so
steeped in the "invoke a method on an object" model that I just couldn't
wrap my head around generic functions. In fact, I remember that when I
first learned OOP I was told that one of the benefits of OOP was that it
bound a data structure and the code to manipulate it together.
Unlearning this was difficult at first.

Having worked with CLOS more now, I really appreciate the CLOS model. I
found that the key was thinking of method invocations not as message
sending, or even invoking something "on an object", but rather
dispatching based on some information, some of which may be type
information. In my CLOS "mental model" I just imagine a little
trampoline doing something the equivalent of:

(defun generic-foo (arg1 arg2)
(typecase arg1
(type1 (generic-foo-method1 arg1 arg2)
(type2 (generic-foo-method2 arg1 arg2)))

Yes, I know GF method dispatch is far more complex, but that's basically
the common case.

The nice thing about it is that I don't have to write or manage that
dispatch table directly. I can do it declaratively, which makes code far
more modular. Simply load a file and new methods appear for that generic
function. Those new methods can be in different modules anywhere. I
don't have to have the source code to any of the other methods.

What you finally realize about CLOS is that it's good for more than just
OOP in the classical sense. In addition to method dispatch based on
object type, ala
(generic-foo object param1 param2)
you can also use EQL specializers to create modular dispatch tables.
Now, whenever I find myself wanting to write
(defun func (arg1 arg2)
(ecase arg1
(foo (...))
(bar (...))))

instead I know write

(defmethod func ((arg1 (eql 'foo)) arg2)
...)

(defmethod func ((arg1 (eql 'bar)) arg2)
...)

The nice thing about this is that different modules can all contribute
to the func dispatch table, simply by executing a
(defmethod func ((arg1 (eql 'blah)) arg2)
...)
form.

I didn't appreciate this aspect of CLOS method dispatch until I read
Peter Siebel's Practical Common Lisp. Peter makes extensive use of this
technique and I have come to appreciate it a lot.

In summary, remember the word "dispatch" and you'll be better for it
when trying to wrap your head around CLOS.
Post by Tom Locke
In message-passing style OO, it's often possible to simply guess the
method name, and there is often support in the editor for enumerating
the available methods. So writing is easier. Reading is easier too -
that "_to_buffer" in the above example is largely noise. Noise that
adds up pretty quickly.
To a certain extent this is just editor mechanics. It's not quite as
easy as with the message-send model in that it's harder to deduce the
type of object you're trying to invoke a method on before you have
written down the object. In other words, with:
object.|
vs.
(|
where "|" is the carat location, the editor can be smarter about
whittling down the set of applicable methods based on the type of
object. But the editor can obviously still help you out with names in
the CLOS example, too, and SLIME will do it if you just start typing
something (anything).

-- Dave
Dave Long
2007-02-23 18:22:55 UTC
Permalink
Post by Dave Roberts
The nice thing about this is that different modules can all contribute
to the func dispatch table, simply by executing a
(defmethod func ((arg1 (eql 'blah)) arg2)
...)
form.
The most recent place I've run into such a mechanism is (the rather
verbose) Inform 7 (in which "the nearest examined node contacting the
current node" can be both specification and executable code).

It dispatches by the most specific phrase, so one can build up definitions
in a piecewise manner, either providing base cases (having stated the
general recursive case elsewhere):

http://en.literateprograms.org/99_Bottles_of_Beer_(Inform_7)
Post by Dave Roberts
Definition: a wall is empty if its inventory is zero.
To say beer count of (w - an empty wall): say "no more bottles of beer".
To act on (w - an empty wall): say "go to the store to buy some more, ".
or merely exhausting a set of alternatives:

http://en.literateprograms.org/Dijkstra's_algorithm_(Inform_7)
Post by Dave Roberts
now m is examined;
change the total distance of m to the extension of n to m;
now m follows n.
let d be the extension of n to m;
if the total distance of m > d begin;
change the total distance of m to d;
now m follows n;
end if.
(avoiding the 'if' by adding another dispatched phrase left as an exercise
for the reader)

I'd been thinking of this mechanism in pattern matching terms, a la
gopher, haskell, etc., because I'd only been vaguely aware of CLOS as
handling before/after/around, not as providing generic dispatch. What are
the differences between clausal function definitions and lispy
multimethods?

-Dave

(now, if methods choose an appropriate chunk of code on the left depending
upon one part of the data they are hit with from the right, and objects
choose an appropriate slot of data on the right depending upon the code
selector they are hit with from the left, and multimethods dispatch on any
or all of their arguments, would multiobjects be able to evaluate
equationally rather than freely, dispatching on any or all of the
functions/monad to which they have been applied?)
Pascal Costanza
2007-02-24 19:44:48 UTC
Permalink
Post by Dave Long
I'd been thinking of this mechanism in pattern matching terms, a la
gopher, haskell, etc., because I'd only been vaguely aware of CLOS
as handling before/after/around, not as providing generic
dispatch. What are the differences between clausal function
definitions and lispy multimethods?
See http://www.dreamsongs.com/Files/clos-book.pdf [1]


Pascal

[1] Despite the name of this file, this is not a book. ;)
--
Pascal Costanza, mailto:***@p-cos.net, http://p-cos.net
Vrije Universiteit Brussel, Programming Technology Lab
Pleinlaan 2, B-1050 Brussel, Belgium
Joe Marshall
2007-02-22 18:06:55 UTC
Permalink
Post by Tom Locke
I may well have missed it, but in all this talk of generic-function
based OO vs. message-passing OO, what about the issue of namespaces
scoped to objects.
e.g.
buffer.append(x) vs. append_to_buffer(buffer, x)
I believe I am correct in stating that the generic function style
requires the latter? Sharing a single generic function "append" among
the very wide range of objects that might provide a definition isn't
workable.
Why not?

The usual implementation of this sort of thing is a table of methods. Along one
dimension we select by the type of the object and along the other we select by
the method name. In a Simula-style implementation, the table is
usually sliced up
according to type, but in a CLOS-style implementation it is sliced up
by message.
--
~jrm
m***@newhall.net
2007-02-23 17:42:16 UTC
Permalink
Post by Pascal Costanza
Sooner or later, the libraries of some language will grow bigger, and
then sooner or later, you need objects that adhere to incompatible
interfaces (messages that happen to have the same name but are used for
different purposes). You can then either choose to distribute the
functionality across several objects, and get object identity problems
along the way, or you can choose to incorporate proper namespace
management.

This is a weak argument, because that point is 'sooner' with
CLOS-style generics and 'later' with OO namespaces. You can argue
that many OO languages lack 'proper' namespace management, that is, a
single elegant solution that solves everything, but if this lack shows
up further on the scaling path than in CLOS, it indicates that the
modularization provided by objects may not be perfect but it *is* a
partial solution that may be good enough 90% of the time. It may also
be possible to extend or enhance the system to be a 'proper' one --
the idea may not be a mistake that must be undone with a completely
different system. But the semantics of object dispatch seem to mostly
solve a problem that in a CLOS-like world require appeal to a second
mechanism.

You can also put aside pragmatics and argue that providing namespaces
automatically with objects is a natural and desirable feature.
Scoping to the object may not always be the right thing to do (and
thus may be overused) but it is hard to imagine that it is not
generally useful.

DING! I declare this round goes to object dispatch.
Matt Knox
2007-02-23 17:56:35 UTC
Permalink
Post by Pascal Costanza
Post by Pascal Costanza
Sooner or later, the libraries of some language will grow bigger, and
then sooner or later, you need objects that adhere to incompatible
interfaces (messages that happen to have the same name but are used for
different purposes). You can then either choose to distribute the
functionality across several objects, and get object identity problems
along the way, or you can choose to incorporate proper namespace
management.
This is a weak argument, because that point is 'sooner' with
CLOS-style generics and 'later' with OO namespaces. You can argue
that many OO languages lack 'proper' namespace management, that is, a
single elegant solution that solves everything, but if this lack shows
up further on the scaling path than in CLOS, it indicates that the
modularization provided by objects may not be perfect but it *is* a
partial solution that may be good enough 90% of the time. It may also
be possible to extend or enhance the system to be a 'proper' one --
the idea may not be a mistake that must be undone with a completely
different system. But the semantics of object dispatch seem to mostly
solve a problem that in a CLOS-like world require appeal to a second
mechanism.
You can also put aside pragmatics and argue that providing namespaces
automatically with objects is a natural and desirable feature.
Scoping to the object may not always be the right thing to do (and
thus may be overused) but it is hard to imagine that it is not
generally useful.
DING! I declare this round goes to object dispatch.
If I understand it correctly, your point is that if you need the draw
method from gun, picture, blood, and duel modules, that in an
object-dispatch system, you can say:

gun.draw args
duel.draw args
...etc.

instead of
(draw_gun gun args)

But it seems that it should be trivial to have, in a CLOS-style generic
function system, 'draw' bound to a function that would look at the type of
its first argument and dispatch to the appropriate GF. Then you'd have:

(draw gun args)
...
and the only differences would be syntax and the ease with which
autocompletion could be done.

Am I missing something?
--
(def (eval e l d c)
(if (atom? e)
((ahandler (type e)) e l d c)
(eval (car e) l d
(fun (x)
(evapp x (cdr e) l d c)))))
Tom Locke
2007-02-23 18:11:54 UTC
Permalink
Post by Matt Knox
But it seems that it should be trivial to have, in a CLOS-style
generic function system, 'draw' bound to a function that would look
at the type of its first argument and dispatch to the appropriate
(draw gun args)
This is exactly what I've been wondering about. It's essentially a
hybrid of object-dispatch and GF. I have a feeling though, that the
CLOS proponents will object to the special status afforded to the
first arg.

Anyone see any problems with this?

T.

p.s. I think Mike hit the nail on the head - it's about pragmatism
not absolutes. As you said yourself recently Pascal, language design
is all about convenience, and shorter, more readable names are more
convenient, even if there are occasions when you have to fall back to
a more verbose style.
Pascal Costanza
2007-02-24 01:16:57 UTC
Permalink
Post by Tom Locke
Post by Matt Knox
But it seems that it should be trivial to have, in a CLOS-style
generic function system, 'draw' bound to a function that would
look at the type of its first argument and dispatch to the
(draw gun args)
This is exactly what I've been wondering about. It's essentially a
hybrid of object-dispatch and GF. I have a feeling though, that the
CLOS proponents will object to the special status afforded to the
first arg.
In CLOS, the first argument has more precedence in determining method
specificity than later arguments, so in this sense, CLOS already
gives the first argument a special status. (This is also called
asymmetric dispatch. In Dylan, dispatch is symmetric. The
disadvantage of symmetric disptach is that it may signal errors when
it cannot resolve method specificity.)
Post by Tom Locke
Anyone see any problems with this?
I find it counter-intuitive to use the same function for vastly
different purposes, no matter whether it is a generic one (in the
CLOS sense) or not.
Post by Tom Locke
T.
p.s. I think Mike hit the nail on the head - it's about pragmatism
not absolutes. As you said yourself recently Pascal, language
design is all about convenience, and shorter, more readable names
are more convenient, even if there are occasions when you have to
fall back to a more verbose style.
Compared to the expresive power that generic functions buy me, I
remain very unexcited about the naming issue.


Pascal
--
Pascal Costanza, mailto:***@p-cos.net, http://p-cos.net
Vrije Universiteit Brussel, Programming Technology Lab
Pleinlaan 2, B-1050 Brussel, Belgium
Douglas Philips
2007-02-24 01:52:20 UTC
Permalink
Post by Pascal Costanza
Post by Tom Locke
Anyone see any problems with this?
I find it counter-intuitive to use the same function for vastly
different purposes, no matter whether it is a generic one (in the
CLOS sense) or not.
Hear, hear!

The "canonical" example, say of "draw" in a graphics system unifies a
concept across multiple "drawable" objects. That one can pun and also
refer to unholstering or to phlebotomize with the same term is to
equivocate over the name, perhaps even disingenuously.
Post by Pascal Costanza
Post by Tom Locke
T.
p.s. I think Mike hit the nail on the head - it's about pragmatism
not absolutes. As you said yourself recently Pascal, language
design is all about convenience, and shorter, more readable names
are more convenient, even if there are occasions when you have to
fall back to a more verbose style.
Compared to the expresive power that generic functions buy me, I
remain very unexcited about the naming issue.
Agreed. Naming is hard, and all the good and short names are taken. :-)

Further, to say that object_o.draw( a, b, c ) somehow magically
disambiguates "draw" is misleading. The types and number of accepted
parameters to 'draw' depend on knowing the type of "object_o"... and
while one may not know the exact type of 'object_o', one does need to
know what species or kingdom it belongs to in order to "ask it to
'draw' itself".

I agree that shorter/smaller code is easier to understand than long
wordy code. There are much better ways to get shorter/smaller code
that is easier to read code than by using non-transparent non-local
type information to shorten one word. But if you don't have the
experience with those other methods, you might be stuck in the Blub
paradox(1), arguing against something you don't fully understand.
Well, I've only been 3/4ths following this, but Pascal's reaction to
the naming issue struct a resonant chord...

(1) - http://www.paulgraham.com/avg.html

--Doug
m***@newhall.net
2007-02-24 03:05:14 UTC
Permalink
Post by Pascal Costanza
Post by Tom Locke
Post by Matt Knox
(draw gun args)
This is exactly what I've been wondering about. It's essentially a
hybrid of object-dispatch and GF. I have a feeling though, that the
CLOS proponents will object to the special status afforded to the
first arg.
In CLOS, the first argument has more precedence in determining method
specificity than later arguments, so in this sense, CLOS already
gives the first argument a special status...
I think what was meant was special status to the first arg *after the
function*, that is, the second arg from a LISP perspective. This is
very different from what CLOS does. It is semantically equivalent to
object dispatch.
Post by Pascal Costanza
I find it counter-intuitive to use the same function for vastly
different purposes, no matter whether it is a generic one (in the
CLOS sense) or not.
Absolutely. But in object dispatch obj1.foo and obj2.foo are not
expected to do the same thing since the two foo's clearly are from two
different namespaces, if the objects are unrelated, and thus are
conceptually different words, whereas (foo obj1) and (foo obj2) are
necessarily from the same namespace, without additional
disambiguation, possibly non-local.
Post by Pascal Costanza
Compared to the expresive power that generic functions buy me, I
remain very unexcited about the naming issue.
Generic functions are not the same thing as generic dispatch. Nobody
is arguing that anyone give up generic functions.
Joe Marshall
2007-02-24 03:19:09 UTC
Permalink
Post by m***@newhall.net
But in object dispatch obj1.foo and obj2.foo are not
expected to do the same thing since the two foo's clearly are from two
different namespaces, if the objects are unrelated, and thus are
conceptually different words, whereas (foo obj1) and (foo obj2) are
necessarily from the same namespace, without additional
disambiguation, possibly non-local.
I see very little different between "obj1.foo" and "(foo obj1)"
The noun in each is clearly obj1 and the verb is foo. There is nothing
magic about the dot or the parenthesis.
--
~jrm
m***@newhall.net
2007-02-24 03:37:33 UTC
Permalink
Post by Joe Marshall
I see very little different between "obj1.foo" and "(foo obj1)"
The noun in each is clearly obj1 and the verb is foo. There is nothing
magic about the dot or the parenthesis.
Yes, there is: this discussion is about method dispatch, and
semantically they are polar opposites. In generic dispatch the verb is
primary, in object dispatch the noun is primary. These choices lead to
nontrivially different object systems.
Joe Marshall
2007-02-24 03:45:41 UTC
Permalink
Post by m***@newhall.net
Post by Joe Marshall
I see very little different between "obj1.foo" and "(foo obj1)"
The noun in each is clearly obj1 and the verb is foo. There is nothing
magic about the dot or the parenthesis.
Yes, there is: this discussion is about method dispatch, and
semantically they are polar opposites. In generic dispatch the verb is
primary, in object dispatch the noun is primary. These choices lead to
nontrivially different object systems.
What do you mean by `primary'? Is it an observable property?

When you are selecting a method through a two-dimensional dispatch (which you
must be doing regardless because that is the dimension of the method space)
it makes not one whit of difference if you choose the row first or the
column first,
you still end up at the same location.
--
~jrm
m***@newhall.net
2007-02-24 03:54:29 UTC
Permalink
Post by Joe Marshall
What do you mean by `primary'? Is it an observable property?
When you are selecting a method through a two-dimensional dispatch (which you
must be doing regardless because that is the dimension of the method space)
it makes not one whit of difference if you choose the row first or the
column first,
you still end up at the same location.
Hmm, not sure where the misunderstanding is here. I think it's pretty
clear: with obj1.foo, I look in the namespace associated with 'obj1'.
With obj2.foo, I look in the namespace associated with 'obj2'. With
(foo obj1), I look in the same namespace that functions are looked up
in. With (foo obj2), I also look in the same namespace functions are
looked up in. In the latter two cases, it's the same namespace. In
the former two cases, they are different namespaces.

It's not a grid, but a graph; cadr is not the same thing as cdar.
Joe Marshall
2007-02-24 04:16:27 UTC
Permalink
Again I ask, is it an observable property? Is there code I can write that
gives me different results depending on whether I look up the method
by using the object first or the method name first?
Post by m***@newhall.net
Post by Joe Marshall
What do you mean by `primary'? Is it an observable property?
When you are selecting a method through a two-dimensional dispatch (which you
must be doing regardless because that is the dimension of the method space)
it makes not one whit of difference if you choose the row first or the
column first,
you still end up at the same location.
Hmm, not sure where the misunderstanding is here. I think it's pretty
clear: with obj1.foo, I look in the namespace associated with 'obj1'.
With obj2.foo, I look in the namespace associated with 'obj2'.
Not clear to me at all. We have two items: an object of some type and
a name `foo'. We have a function (the dispatch function) that maps from
the tuple of object types and names to invokable methods. Essentially,
obj1.foo is evaluated as this:
(funcall (select-method (type-of obj1) 'foo) obj1)

Now it is *usually* the case that select-method works by inspecting a table
that is manifestly part of the type descriptor, but that is an implementation
detail. It would just as well if stored the methods on the property list of the
method name indexed by the type name. We depend on the types of different
objects being distinguishable (this almost goes without saying).

It would *also* work just as well if we stored the methods in a lexical closure
somewhere and arranged for that closure to be placed in the function cell of
the symbol so that the dispatch happened through the function call mechanism.
Essentially I am taking `select-method' and swapping the arguments:

(funcall (select-method 'foo (type-of obj1)) obj1)

and then currying

(funcall (funcall (curried-select-method 'foo) (type-of obj1)) obj1)

and simply implemented `curried-select-method' as `symbol-function'.

Obviously re-ordering the arguments to a function makes no difference if I
re-order them at all call sites, and currying a function makes no difference
if I arrange for the call sites to use the curried form. Semantically, these
are identical.
Post by m***@newhall.net
With
(foo obj1), I look in the same namespace that functions are looked up
in. With (foo obj2), I also look in the same namespace functions are
looked up in. In the latter two cases, it's the same namespace. In
the former two cases, they are different namespaces.
It's not a grid, but a graph; cadr is not the same thing as cdar.
No, but if I swap all calls to CADR with calls to CDAR and swap the
contents of all cadrs with cdrs, there is no observable difference.
It doesn't matter how you get to the cell in question, it matters what
the contents are.
--
~jrm
m***@newhall.net
2007-02-24 04:34:59 UTC
Permalink
Post by Joe Marshall
Again I ask, is it an observable property? Is there code I can write that
gives me different results depending on whether I look up the method
by using the object first or the method name first?
I think we are talking past each other. You may be right for the
problem you are focusing on but it has nothing to do with the
discussion at hand. The question is about namespace collisions. In
generic dispatch two methods named 'foo' on two different objects
share a namespace (modulo package), in object dispatch they do not
(modulo inheritance). You can demonstrate this easily and trivially
in real code. It is an observable property in languages with the two
different kinds of dispatch.
Joe Marshall
2007-02-24 04:56:11 UTC
Permalink
Post by m***@newhall.net
Post by Joe Marshall
Again I ask, is it an observable property? Is there code I can write that
gives me different results depending on whether I look up the method
by using the object first or the method name first?
I think we are talking past each other.
We may be.
Post by m***@newhall.net
The question is about namespace collisions.
What is the effect of this `collision'? Suppose I have two unrelated types
T1 and T2 and instances I1 and I2 of the respective types. Now suppose
a method named foo is define on each type. In neither CLOS nor in Java
is there a problem.
Post by m***@newhall.net
In
generic dispatch two methods named 'foo' on two different objects
share a namespace (modulo package), in object dispatch they do not
(modulo inheritance). You can demonstrate this easily and trivially
in real code. It is an observable property in languages with the two
different kinds of dispatch.
Well I wish you would supply some example code that demonstrates
the difference.
--
~jrm
m***@newhall.net
2007-02-24 05:03:25 UTC
Permalink
Post by Joe Marshall
What is the effect of this `collision'? Suppose I have two unrelated types
T1 and T2 and instances I1 and I2 of the respective types. Now suppose
a method named foo is define on each type. In neither CLOS nor in Java
is there a problem.
Refer to the beginning of this thread.
Douglas Philips
2007-02-24 04:05:23 UTC
Permalink
Post by m***@newhall.net
Yes, there is: this discussion is about method dispatch, and
semantically they are polar opposites. In generic dispatch the verb is
primary, in object dispatch the noun is primary. These choices lead to
nontrivially different object systems.
Ah yes, the Kingdom of the Nouns.
http://steve-yegge.blogspot.com/2006/03/execution-in-kingdom-of-
nouns.html

----
m***@newhall.net
2007-02-24 04:25:37 UTC
Permalink
Post by Douglas Philips
Ah yes, the Kingdom of the Nouns.
http://steve-yegge.blogspot.com/2006/03/execution-in-kingdom-of-
nouns.html
Are all languages that use object dispatch on a par with Java?
(Python, for example?)

I think you are appealing to Logical Fallacy #41, the Straw Man argument:

http://www.nizkor.org/features/fallacies/straw-man.html
Douglas Philips
2007-02-24 04:32:54 UTC
Permalink
Post by m***@newhall.net
Are all languages that use object dispatch on a par with Java?
(Python, for example?)
Hardly, Python admits of ("horrors!") naked functions.
Post by m***@newhall.net
http://www.nizkor.org/features/fallacies/straw-man.html
Now who is it that is humor impaired?
"Everything is a noun" "Everything is a verb" are both extremes that
are less than useful most of the time, esp. in the world you posit of
ever increasing software complexity nee scalability. (Erlang is the
Jester of that court, but we digress).

--Doug
m***@newhall.net
2007-02-24 04:41:45 UTC
Permalink
Post by Douglas Philips
Hardly, Python admits of ("horrors!") naked functions.
Horrors to whom? No-one on this list I hope.
Post by Douglas Philips
"Everything is a noun" "Everything is a verb" are both extremes that
are less than useful most of the time, esp. in the world you posit of
ever increasing software complexity nee scalability. (Erlang is the
Jester of that court, but we digress).
Is anyone here advocating either extreme?

Then I guess if your link was not in support of your previous argument
(the natural assumption, I think), it was not really relevant to the
discussion but only a humorous aside.
Douglas Philips
2007-02-24 03:24:31 UTC
Permalink
Post by m***@newhall.net
Post by Pascal Costanza
I find it counter-intuitive to use the same function for vastly
different purposes, no matter whether it is a generic one (in the
CLOS sense) or not.
Absolutely. But in object dispatch obj1.foo and obj2.foo are not
expected to do the same thing since the two foo's clearly are from two
different namespaces, if the objects are unrelated, and thus are
conceptually different words, whereas (foo obj1) and (foo obj2) are
necessarily from the same namespace, without additional
disambiguation, possibly non-local.
obj1.foo and obj2.foo are not disambiguated by obj1 or obj2, they are
disambiguated by the types of obj1 and obj2. Information which is
_not_ stated at the point, but yet which is required so that the
programmer will pass the correct number and type of parameters to
obj1.foo( ) and obj2.foo( ).

If you object to (image:draw obj1 ...) vs. (holster:draw obj2 ...)
because having to indicate the 'source' of draw you wish to invoke is
wordier, I agree with Pascal on that. Yawn. I'd rather see the intent
made clear in ambiguous cases (and hence this is applicable to
Pascal's Java MI example), for I have much more powerful ways to
condense my code than saving one word by extracting the type of obj1/
obj2.

--Doug
m***@newhall.net
2007-02-24 03:47:40 UTC
Permalink
Post by Douglas Philips
If you object to (image:draw obj1 ...) vs. (holster:draw obj2 ...)
because having to indicate the 'source' of draw you wish to invoke is
wordier, I agree with Pascal on that. Yawn. I'd rather see the intent
made clear in ambiguous cases (and hence this is applicable to
Pascal's Java MI example), for I have much more powerful ways to
condense my code than saving one word by extracting the type of obj1/
obj2.
I think you mistakenly trivialize the issue at hand by
misunderstanding it as a complaint about terseness. If we are to go
down that (specious) road, you will not have to go far to find dynamic
typing sceptics that would prefer all types be explicitly stated, just
to make things clear.

Whether to use generic or object dispatch is a fundamental language
design decision that affects the entire structure of the object
system. Which one is better is of serious concern. No language
feature today can escape the question of scalability, as software is
not getting simpler and smaller over time. If generic dispatch has a
flaw that becomes more apparent as software systems get more complex,
it is not a viable option for future language designs.
Douglas Philips
2007-02-24 05:03:56 UTC
Permalink
Post by m***@newhall.net
Post by Douglas Philips
If you object to (image:draw obj1 ...) vs. (holster:draw obj2 ...)
because having to indicate the 'source' of draw you wish to invoke is
wordier, I agree with Pascal on that. Yawn. I'd rather see the intent
made clear in ambiguous cases (and hence this is applicable to
Pascal's Java MI example), for I have much more powerful ways to
condense my code than saving one word by extracting the type of obj1/
obj2.
I think you mistakenly trivialize the issue at hand by
misunderstanding it as a complaint about terseness. If we are to go
down that (specious) road, you will not have to go far to find dynamic
typing sceptics that would prefer all types be explicitly stated, just
to make things clear.
Not types, type hierarchies. You know full well that "draw" in the
"image concept space" has parameters that are different in meaning
from the parameters to "draw" in the "holstered things concept
space". No amount of name-space magic is going to alleviate you from
having to know which sort of 'draw' you are intending to invoke.
Post by m***@newhall.net
Whether to use generic or object dispatch is a fundamental
language
design decision that affects the entire structure of the object
system. Which one is better is of serious concern. No language
feature today can escape the question of scalability, as software is
not getting simpler and smaller over time. If generic dispatch has a
flaw that becomes more apparent as software systems get more complex,
it is not a viable option for future language designs.
You seem to see the generic dispatch as a problem only because you
insist that 'image draw' and 'holster draw' pun to the same bare
symbol 'draw'. ... That somehow the world of increasing scalability
admits of no name space management beyound object dispatch? ... That
somehow we'll have, what, dozens of 'draw' methods in the same code
and that we'll pierce the scales of complexity because we'll "know"
the types of the objects to which we are applying these eponymous
methods with abandon?

Pascal has already shown that this is really about name space
control. None of us are saying that 'image:draw' should be
'triangle:draw' or 'square:draw' or 'polygon:draw' or 'circle:draw'
because we all know that the nature of that draw is what matters, not
any specific implementation.

You seem to be insisting that we can't tame complexity unless saying
'object_h.draw' gets a holstered draw while saying 'object_i.draw'
gets an image draw, because saying (holster:draw object_h ...) or
(image:draw object_i ...) doesn't scale?

I don't see your point. If there are ten or a hundred, or just five
different kinds of draw methods all congregating in one function/
module/subsystem, what you have to manage is the mental burden of
keeping track of them. Object dispatch isn't going help, because not
writing x:draw doesn't save you _from needing to know that it is x's
draw you want to invoke_.

We seem to be at an impasse. You seem to be insisting that object
based dispatch scales "better" and that generic dispatch has to be
without any name space control and so doesn't scale. And "we" are
unsuccessful at convincing you to let go of the local maxima of
object based dispatch for a much more powerful mechanism , and the
issue seems to be unconvincing because you have to deal directly with
the name space issue.

Put another way: As Pascal and others have pointed out, the dynamic
dispatch that doesn't favor on particular parameter over all the
others is a huge win. But yes, that means you have to abandon the
apparent "free" name space dispatch. But that is just a local maxima.
Perhaps one has to experience a richer dispatch in a non-trivial
environment to to understand that. (see previous message link re:
Blub paradox).

--Doug
m***@newhall.net
2007-02-24 05:13:25 UTC
Permalink
Post by Douglas Philips
We seem to be at an impasse. You seem to be insisting that object
based dispatch scales "better" and that generic dispatch has to be
without any name space control and so doesn't scale. And "we" are
unsuccessful at convincing you to let go of the local maxima of
object based dispatch for a much more powerful mechanism , and the
issue seems to be unconvincing because you have to deal directly with
the name space issue.
There are so many misunderstandings and misrepresentations in your
post, I am daunted by the task of correcting them collectively.
Suffice it to say I do not believe what you think I do. If you are
interested at all in my actual position, please re-read the prior
posts on this thread where all has been said before.
Douglas Philips
2007-02-24 05:39:28 UTC
Permalink
Post by m***@newhall.net
Post by Douglas Philips
We seem to be at an impasse. You seem to be insisting that object
based dispatch scales "better" and that generic dispatch has to be
without any name space control and so doesn't scale. And "we" are
unsuccessful at convincing you to let go of the local maxima of
object based dispatch for a much more powerful mechanism , and the
issue seems to be unconvincing because you have to deal directly with
the name space issue.
There are so many misunderstandings and misrepresentations in your
post, I am daunted by the task of correcting them collectively.
Suffice it to say I do not believe what you think I do. If you are
interested at all in my actual position, please re-read the prior
posts on this thread where all has been said before.
Post by Douglas Philips
Sooner or later, the libraries of some language will grow bigger, and
then sooner or later, you need objects that adhere to incompatible
interfaces (messages that happen to have the same name but are used for
different purposes). You can then either choose to distribute the
functionality across several objects, and get object identity
problems
along the way, or you can choose to incorporate proper namespace
management.
This is a weak argument, because that point is 'sooner' with
CLOS-style generics and 'later' with OO namespaces. You can argue
that many OO languages lack 'proper' namespace management, that is, a
single elegant solution that solves everything, but if this lack shows
up further on the scaling path than in CLOS, it indicates that the
modularization provided by objects may not be perfect but it *is* a
partial solution that may be good enough 90% of the time. It may also
be possible to extend or enhance the system to be a 'proper' one --
the idea may not be a mistake that must be undone with a completely
different system. But the semantics of object dispatch seem to mostly
solve a problem that in a CLOS-like world require appeal to a second
mechanism.
The whole mess in C++ as highlighted by the '<<' and '>>' operators
is a pretty clear indication that it is a mistake.
Post by m***@newhall.net
You can also put aside pragmatics and argue that providing
namespaces
automatically with objects is a natural and desirable feature.
Scoping to the object may not always be the right thing to do (and
thus may be overused) but it is hard to imagine that it is not
generally useful.
And has been pointed out "earlier in the thread", you can do single
method dispatch, it is a proper subset, so desire it away, just
because you can do multiple parameter dispatch doesn't mean you
always have to.


So again, just because you can call 'image:draw' with
'image_instance.draw' and 'hoslter:draw' with 'holster_instace.draw'
doesn't alleviate the need for you to know which class hierarchy's
draw you are intending to invoke. generic dispatch is strictly more
powerful than object(single parameter) dispatch. You get a little bit
of gain (for a while) from apparently free name space management, but
the complexity problem isn't about having to type a class-heirarchy
qualifier to indicate which brand of 'draw' you want to invoke, it is
all about having multiple brands all in scope at once.

--Doug
Tom Locke
2007-02-24 11:59:26 UTC
Permalink
Nice to see this topic has generated some interest :-)

Can I break this down into words of two syllables or less?

The following (darn - blew it already) guide:

http://www.aiai.ed.ac.uk/~jeff/clos-guide.html

Contains:
------
A defclass that provided similar access functions, etc, would be:

(defclass person ()
((name :accessor person-name
:initform 'bill
:initarg :name)
(age :accessor person-age
:initform 10
:initarg :age)))
------

Is this good CLOS or bad CLOS? How about we rename those accessors to
"name" and "age"? Good or bad?

There is some interesting discussion of this point in the context of
Dylan here:

http://www.opendylan.org/books/dpg/db_125.html

Quote: "Therefore, for getters that you intend to export, it makes
sense prevent clashes by considering the name of the slot carefully.
One technique is to prefix the name of the property with the name of
the class"

And a couple of general points...

In practice, programmers using object-dispatch languages have no
difficulty understanding where a call is going when the see something
like "file.name". Yes the namespace is implicit rather than explicit,
but people are good at context! Honestly, this is a BIG reason why
people like object-dispatch. It is. Honestly.

Can we try to avoid this degenerating into "my language is better
than your language"? The question at hand relates to a very specific
comparison of the two styles. The answer I'm hoping for is either
"no, this is not a valid advantage of object-dispatch because ...",
or "yes, that's one advantage object-dispatch has". If the answer
turns out to be the latter, feel free to append "but overall I still
prefer generic-functions because ...", if that makes you happier :-)

Tom
Pascal Costanza
2007-02-24 14:43:21 UTC
Permalink
Post by Tom Locke
Nice to see this topic has generated some interest :-)
:)
Post by Tom Locke
Can I break this down into words of two syllables or less?
http://www.aiai.ed.ac.uk/~jeff/clos-guide.html
------
(defclass person ()
((name :accessor person-name
:initform 'bill
:initarg :name)
(age :accessor person-age
:initform 10
:initarg :age)))
------
Is this good CLOS or bad CLOS?
Hard to tell. It's a toy example.

In general, it's not a good idea to indicate the type name in
accessors, but leave it to the package / module mechanism to
disambiguate names. If you had a statically typed language with
generic functions, it may also be an option to leave it the type
system (but I am not aware of such a language).

On the other hand, there is for example the CLOS MOP that uses this
style of naming accessors (class-precedence-list, class-slots,
generic-function-methods, etc.).
Post by Tom Locke
How about we rename those accessors to "name" and "age"? Good or bad?
Probably good.
Post by Tom Locke
There is some interesting discussion of this point in the context
http://www.opendylan.org/books/dpg/db_125.html
Quote: "Therefore, for getters that you intend to export, it makes
sense prevent clashes by considering the name of the slot
carefully. One technique is to prefix the name of the property with
the name of the class"
I can't comment on this because I don't know Dylan that well.
Post by Tom Locke
And a couple of general points...
In practice, programmers using object-dispatch languages have no
difficulty understanding where a call is going when the see
something like "file.name". Yes the namespace is implicit rather
than explicit, but people are good at context! Honestly, this is a
BIG reason why people like object-dispatch. It is. Honestly.
I don't buy into this. For me, the big win of OOP always was ad-hoc
polymorphism, i.e., the ability to invoke different variations of the
conceptually same operation without the need to write extensive if-
statements everywhere. It never was about the ability to clutter the
same names with completely unrelated functionality.
Post by Tom Locke
Can we try to avoid this degenerating into "my language is better
than your language"? The question at hand relates to a very
specific comparison of the two styles. The answer I'm hoping for is
either "no, this is not a valid advantage of object-dispatch
because ...", or "yes, that's one advantage object-dispatch has".
If the answer turns out to be the latter, feel free to append "but
overall I still prefer generic-functions because ...", if that
makes you happier :-)
No, "it" is not a valid advantage of message sending. ;)

Even some Smalltalk implementations have introduced constructs for
explicit namespace control. I guess they had reasons for doing so.


Pascal
--
Pascal Costanza, mailto:***@p-cos.net, http://p-cos.net
Vrije Universiteit Brussel, Programming Technology Lab
Pleinlaan 2, B-1050 Brussel, Belgium
Tom Locke
2007-02-24 15:15:03 UTC
Permalink
Post by Pascal Costanza
Post by Tom Locke
In practice, programmers using object-dispatch languages have no
difficulty understanding where a call is going when the see
something like "file.name". Yes the namespace is implicit rather
than explicit, but people are good at context! Honestly, this is a
BIG reason why people like object-dispatch. It is. Honestly.
I don't buy into this. For me, the big win of OOP always was ad-hoc
polymorphism, i.e., the ability to invoke different variations of
the conceptually same operation without the need to write extensive
if-statements everywhere. It never was about the ability to clutter
the same names with completely unrelated functionality.
It is amazing how people insist on arguing in absolutes. Notice how
"A big reason" becomes "THE big win". I didn't for a moment suggest
that this was the main benefit of OO. I'm just saying that this is a
VERY popular aspect of OO with the masses (of course this is
conjecture). In my experience, people like that feature a lot.

Tom
Pascal Costanza
2007-02-24 16:06:09 UTC
Permalink
Post by Tom Locke
Post by Pascal Costanza
Post by Tom Locke
In practice, programmers using object-dispatch languages have no
difficulty understanding where a call is going when the see
something like "file.name". Yes the namespace is implicit rather
than explicit, but people are good at context! Honestly, this is
a BIG reason why people like object-dispatch. It is. Honestly.
I don't buy into this. For me, the big win of OOP always was ad-
hoc polymorphism, i.e., the ability to invoke different variations
of the conceptually same operation without the need to write
extensive if-statements everywhere. It never was about the ability
to clutter the same names with completely unrelated functionality.
It is amazing how people insist on arguing in absolutes. Notice how
"A big reason" becomes "THE big win". I didn't for a moment suggest
that this was the main benefit of OO. I'm just saying that this is
a VERY popular aspect of OO with the masses (of course this is
conjecture). In my experience, people like that feature a lot.
I don't know so many people, so I am not able to tell what is popular
with the masses. I just don't buy into this. However, I think it is
easy to confuse ad-hoc polymorphism with overloading of names.


Pascal
--
Pascal Costanza, mailto:***@p-cos.net, http://p-cos.net
Vrije Universiteit Brussel, Programming Technology Lab
Pleinlaan 2, B-1050 Brussel, Belgium
Mike Newhall
2007-02-24 18:04:08 UTC
Permalink
Post by Tom Locke
In practice, programmers using object-dispatch languages have no
difficulty understanding where a call is going when the see something
like "file.name". Yes the namespace is implicit rather than explicit,
but people are good at context! Honestly, this is a BIG reason why
people like object-dispatch. It is. Honestly.
Hear, hear.
Steve Dekorte
2007-02-25 04:01:49 UTC
Permalink
Post by Tom Locke
In practice, programmers using object-dispatch languages have no
difficulty understanding where a call is going when the see something
like "file.name". Yes the namespace is implicit rather than explicit,
but people are good at context! Honestly, this is a BIG reason why
people like object-dispatch. It is. Honestly.
And not knowing the type isn't just an unwanted side-effect, it's
actually the goal. Dynamic OO is about escaping from fragile and
inflexible type dependencies by using type independent messages
instead of type dependent functions.

- Steve
Douglas Philips
2007-02-25 04:12:19 UTC
Permalink
Post by Steve Dekorte
Post by Tom Locke
In practice, programmers using object-dispatch languages have no
difficulty understanding where a call is going when the see
something
like "file.name". Yes the namespace is implicit rather than
explicit,
but people are good at context! Honestly, this is a BIG reason why
people like object-dispatch. It is. Honestly.
And not knowing the type isn't just an unwanted side-effect, it's
actually the goal. Dynamic OO is about escaping from fragile and
inflexible type dependencies by using type independent messages
instead of type dependent functions.
The Liskov Substitution Principle (1) says that you know the object
will respond to a particular kind of message. While you don't know,
and don't want to know, the exact type, you need to know the family
that the type belongs to, or you the message won't even be coherent.
In this case what you are "typing" is the message, and that you have
to know.

--Doug


(1) http://en.wikipedia.org/wiki/Liskov_substitution_principle
Pascal Costanza
2007-02-25 12:33:28 UTC
Permalink
Post by Steve Dekorte
Post by Tom Locke
In practice, programmers using object-dispatch languages have no
difficulty understanding where a call is going when the see
something
like "file.name". Yes the namespace is implicit rather than
explicit,
but people are good at context! Honestly, this is a BIG reason why
people like object-dispatch. It is. Honestly.
And not knowing the type isn't just an unwanted side-effect, it's
actually the goal. Dynamic OO is about escaping from fragile and
inflexible type dependencies by using type independent messages
instead of type dependent functions.
You still haven't told us how you disambiguate the draw message when
your object inherits both from a weapon and a picture. Especially
when both draw messages have the same signature.


Pascal
--
Pascal Costanza, mailto:***@p-cos.net, http://p-cos.net
Vrije Universiteit Brussel, Programming Technology Lab
Pleinlaan 2, B-1050 Brussel, Belgium
Tom Locke
2007-02-25 13:35:54 UTC
Permalink
Post by Pascal Costanza
Post by Steve Dekorte
And not knowing the type isn't just an unwanted side-effect, it's
actually the goal. Dynamic OO is about escaping from fragile and
inflexible type dependencies by using type independent messages
instead of type dependent functions.
You still haven't told us how you disambiguate the draw message
when your object inherits both from a weapon and a picture.
Especially when both draw messages have the same signature.
If there is type information associated with the *reference*, not the
object, e.g. as in a statically typed language, this can be used to
disambiguate. C# does this.

Dynamically typed languages do indeed run into a problem. As you
pointed out you will have to use something along the lines of an
adapter object, and yes, run into object identity worries.

It is conceivable that you could run into a big problem if you face
just the right interaction between multiple inheritance, polymorphism
and object identity. In practice this seems to be extremely rare. Is
this a big enough deal to dismiss object-dispatch in dynamically
typed languages? I'd say no, but that's more of a judgement call than
an absolute.

What I wish I was more clear on, was how often, in GF based OO, one
ends up using names that object-dispatch people would consider overly
verbose, whether it be draw-gun at the definition site, or gun:draw
at the call site.

Answer: all over the place => big win to object-dispatch

Answer: very rarely => big win to GFs

(note to black-and-white types: I did not say "overall win to ...")

The reality is probably in between, and maybe only a lot of
experience with both styles would shed light (combined with a
detached state-of-mind!).

I think this is a big reason why language design is such a mysterious
art - a deep understanding of the *pragmatic* reality of different
styles is only possible with a lot of experience, and few people have
the time to gain this experience in a wide range of paradigms. Kind
of a mirror of Paul Grahams point that the only way to find out if
someone is a good programmer is to work with them.

Tom

p.s. I'm very glad to see this thread survive a near fatal detour
into flame-dom :-)
Pascal Costanza
2007-02-25 13:54:59 UTC
Permalink
Post by Tom Locke
Post by Pascal Costanza
Post by Steve Dekorte
And not knowing the type isn't just an unwanted side-effect, it's
actually the goal. Dynamic OO is about escaping from fragile and
inflexible type dependencies by using type independent messages
instead of type dependent functions.
You still haven't told us how you disambiguate the draw message
when your object inherits both from a weapon and a picture.
Especially when both draw messages have the same signature.
If there is type information associated with the *reference*, not
the object, e.g. as in a statically typed language, this can be
used to disambiguate. C# does this.
In my first response to your question (February 22, 11:33), I already
said that type systems are used to manage namespaces. That's exactly
what I meant.

And C# goes beyond Java here because it provides ways to disambiguate
cases where Java just blocks. IIUC, C# is similar to C++ in this
regard. So in those border cases, you can say Gun::draw and
Picture::draw, for example. (I don't know the C# syntax off the top
of my head, so I am probably wrong wrt syntactic details.)
Post by Tom Locke
Dynamically typed languages do indeed run into a problem. As you
pointed out you will have to use something along the lines of an
adapter object, and yes, run into object identity worries.
Right.
Post by Tom Locke
It is conceivable that you could run into a big problem if you face
just the right interaction between multiple inheritance,
polymorphism and object identity. In practice this seems to be
extremely rare. Is this a big enough deal to dismiss object-
dispatch in dynamically typed languages? I'd say no, but that's
more of a judgement call than an absolute.
What I wish I was more clear on, was how often, in GF based OO, one
ends up using names that object-dispatch people would consider
overly verbose, whether it be draw-gun at the definition site, or
gun:draw at the call site.
Answer: all over the place => big win to object-dispatch
Answer: very rarely => big win to GFs
(note to black-and-white types: I did not say "overall win to ...")
The reality is probably in between, and maybe only a lot of
experience with both styles would shed light (combined with a
detached state-of-mind!).
Yes, I guess it's in between.

In my experience, the important change in naming conventions comes
from switching between an object-dispatch language vs. a Lisp/Scheme-
style language, not from switching between object-dispatch and
generic functions. I don't notice strong differences between names
for plain functions and generic ones, at least not in Common Lisp.


Pascal
--
Pascal Costanza, mailto:***@p-cos.net, http://p-cos.net
Vrije Universiteit Brussel, Programming Technology Lab
Pleinlaan 2, B-1050 Brussel, Belgium
Robbert Haarman
2007-02-25 17:58:11 UTC
Permalink
Post by Tom Locke
Post by Pascal Costanza
Post by Steve Dekorte
And not knowing the type isn't just an unwanted side-effect, it's
actually the goal. Dynamic OO is about escaping from fragile and
inflexible type dependencies by using type independent messages
instead of type dependent functions.
You still haven't told us how you disambiguate the draw message
when your object inherits both from a weapon and a picture.
Especially when both draw messages have the same signature.
If there is type information associated with the *reference*, not the
object, e.g. as in a statically typed language, this can be used to
disambiguate. C# does this.
I don't know exactly how things work in C#, but I assume about the same
way as in Java. Now, what you're saying sounds like: suppose we have two
classes, Picture and Weapon, and a third class, Example, that (somehow)
inherits the draw method from both of the other classes.

Now, if we have

Picture x =new Example();
x.draw();

we know that draw from Picture is being called, rather than draw from
Weapon.

However, that does mean that we can't use our Example instance as both a
Picture and a Weapon in the same code. One way around that would be

Example x = new Example();
((Picture) x).draw();
((Weapon) x).draw());

...and now you're explicitly indicating the namespace, just like you
would in

(picture:draw x)
(weapon:draw x)

except that the Java code smacks a bit kludgy to me.
Post by Tom Locke
It is conceivable that you could run into a big problem if you face
just the right interaction between multiple inheritance, polymorphism
and object identity. In practice this seems to be extremely rare. Is
this a big enough deal to dismiss object-dispatch in dynamically
typed languages? I'd say no, but that's more of a judgement call than
an absolute.
I feel that argument is somewhat backwards. We're not dismissing
object dispatch because of namespace issues, but because generic
functions are an alternative that is more powerful (I also find them
more elegant, but I recognize that is subjective).

The argument about name clashes was brought forward as a criticism of
generic functions, which were supposed to exist in a single namespace,
whereas, in object dispatch, method names are prefixed by an object, and
thus divided among multiple namespaces. If anything, object dispatch
would have an _advantage_ over generic functions here, so it could never
be a reason to dismiss object dispatch.

Having said that, I don't think the "object dispatch provides more
namespaces than generic functions" really makes sense. Whether you write
x.foo or (foo x) is just a matter of syntax. In as far as the type of x
helps disambiguate foo in the object dispatch case, it also helps
disambiguate foo in the generic function case.

In CLOS, methods must abide by certain restrictions set out by the
generic function. For example, the number of arguments must be
compatible. It is not allowed to define a method that takes three
arguments for a generic function that only takes two. However, this is
not an inherent property of generic functions; it's just a limitation of
CLOS. Without that restriction, I don't see how object dispatch
disambiguates more cases than generic functions. Generic functions can
dispatch on the type of their first argument every bit as well as object
dispatch.
Post by Tom Locke
What I wish I was more clear on, was how often, in GF based OO, one
ends up using names that object-dispatch people would consider overly
verbose, whether it be draw-gun at the definition site, or gun:draw
at the call site.
I think that is more a matter of culture than one of technical
necessity. For example, names like point-x are common in Lisp programs,
but not technically necessary (and often abbreviated locally by macros,
e.g. with-slots). Similarly, I find many (what I consider) overly
verbose names in Java code that are not dictated by technical necessity
as far as I know.

Also, as already pointed out, the real savings don't come from using
shorter identifiers, but from language features that allow programs to
contain fewer identifiers in the first place.

By and large, I agree with Pascal:

1. Generic functions are desirable. Compared to object dispatch, generic
functions are more powerful (and expressive).

2. Name clashes can arise. Since this happens regardless of how
dispatching is done, it is wise to make the namespace facility be
orthoganal to dispatching, as well.
Post by Tom Locke
I think this is a big reason why language design is such a mysterious
art - a deep understanding of the *pragmatic* reality of different
styles is only possible with a lot of experience, and few people have
the time to gain this experience in a wide range of paradigms.
The task is made even more difficult by the fact that , even if
something is superior, it will not always be recognized as such. People
will still cling to the way they're used to doing things, and they might
complain about your language perceived shortcomings loud enough that it
keeps other people away from your language. In the end, I think the best
you can do is make the language the best language for yourself. That
way, at least you can be happy. The rest of the world can take it or
leave it.

Regards,

Bob
--
"Trying to make bits uncopyable is like trying to make water not wet. The
sooner people accept this, and build business models that take this into
account, the sooner people will start making money again."
-- Bruce Schneier
Joe Marshall
2007-02-26 20:20:45 UTC
Permalink
Post by Robbert Haarman
Having said that, I don't think the "object dispatch provides more
namespaces than generic functions" really makes sense. Whether you write
x.foo or (foo x) is just a matter of syntax. In as far as the type of x
helps disambiguate foo in the object dispatch case, it also helps
disambiguate foo in the generic function case.
Exactly. What I am trying to get across (without much success) is that
with `object dispatch' you look up the type first, then find the method name,
whereas in `generic dispatch' you look up the name first and then find the type.
In either case the space of possible combinations is identical.
--
~jrm
Steve Dekorte
2007-02-26 01:38:37 UTC
Permalink
Post by Pascal Costanza
Post by Steve Dekorte
Post by Tom Locke
In practice, programmers using object-dispatch languages have no
difficulty understanding where a call is going when the see
something
like "file.name". Yes the namespace is implicit rather than
explicit,
but people are good at context! Honestly, this is a BIG reason why
people like object-dispatch. It is. Honestly.
And not knowing the type isn't just an unwanted side-effect, it's
actually the goal. Dynamic OO is about escaping from fragile and
inflexible type dependencies by using type independent messages
instead of type dependent functions.
You still haven't told us how you disambiguate the draw message
when your object inherits both from a weapon and a picture.
Especially when both draw messages have the same signature.
Pascal,

If you're asking me, it depends on the particular object model. IMO,
a proper object model has a non-ambiguous lookup path. For example,
in Io, the object looks in itself, then in it's protos list in a
depth first order. So there is no confusion with regards to finding
slots - the first in the lookup path is always the one found.

Also, the slot name is the only thing that matters - all signatures
in Io are effectively the same (they are pass variable object lists
and return one object). And there is no notion of globals - all
lookups start at the message receiver (which may be a locals object)
and proceed from there.

- Steve
Douglas Philips
2007-02-24 15:07:03 UTC
Permalink
Post by Tom Locke
Is this good CLOS or bad CLOS? How about we rename those accessors
to "name" and "age"? Good or bad?
Question is underspecified. The assumption at work here is that there
is just one big glommed together name-space. Pascal has already
attempted to address that issue. Single object dispatch lets you
pretend that you don't have name space issues, but that doesn't
reduce the cognitive load on a programmer faced with a module in
which there are many different kinds/species of 'draw' methods being
invoked.
Post by Tom Locke
And a couple of general points...
In practice, programmers using object-dispatch languages have no
difficulty understanding where a call is going when the see
something like "file.name". Yes the namespace is implicit rather
than explicit, but people are good at context! Honestly, this is a
BIG reason why people like object-dispatch. It is. Honestly.
The examples we've been using are so toy that such a simplistic
approach works. Almost. If you look at, say, C++'s dispatch, you'll
find that "cout << myObject" (cout.operator<<(myObject)) is not what
you want. Given the number of papers you can find via <search engine
du jour> on multi-dispatch this is hardly a trivial issue and it is
not limited to any particular language's implementation of object
dispatch. It is a fundamental limitation of single parameter ("the"
object) dispatch. The Visitor Pattern and other sorts of manual multi-
dispatch may be ways to work around it, but that is just trading
programmer time and effort to overcome a language deficiency.

Regardless, 'file.name' is a bit disingenuous. It is using the name
of the variable to convey type information to a human reader. The
compiler has no such bias. Hence the abuse of this technique in
obsfucated code. So while you find it OK to say 'file.name', you
object to '(file:name myObject ...)'? I am not clear on what the
actual grounds for objection are. There is no need to qualify 'name'
in a module/function/etc. unless there is ambiguity about which
species of 'name' you want.


But this is approaching the pointless. Quoting Stroustrup:
<http://www.research.att.com/~bs/bs_faq.html#compare>
"I also worry about a phenomenon I have repeatedly observed in honest
attempts at language comparisons. The authors try hard to be impartial,
but are hopelessly biased by focusing on a single application, a single
style of programming, or a single culture among programmers.
Worse, when one language is significantly better known than others,
a subtle shift in perspective occurs: Flaws in the well-known language
are deemed minor and simple workarounds are presented, whereas similar
flaws in other languages are deemed fundamental. Often, the workarounds
commonly used in the less-well-known languages are simply unknown to
the
people doing the comparison or deemed unsatisfactory because they would
be unworkable in the more familiar language."


--Doug
m***@newhall.net
2007-02-23 18:28:31 UTC
Permalink
Post by Matt Knox
If I understand it correctly, your point is that if you need the draw
method from gun, picture, blood, and duel modules, that in an
gun.draw args
duel.draw args
...etc.
instead of
(draw_gun gun args)
But it seems that it should be trivial to have, in a CLOS-style generic
function system, 'draw' bound to a function that would look at the type of
(draw gun args)
...
and the only differences would be syntax and the ease with which
autocompletion could be done.
Am I missing something?
No, I think that is correct, but that is not how CLOS-style generic
OOP works, the original subject of criticism. You could in fact say
that that is just syntactic sugar on the syntax "object.method(args)",
rewritten "(method object args)". But it also brings up additional
design issues like, how do you decide which symbols refer to methods
and thus when to look at the arguments before looking at the signature
of the function-position symbol (which you presumably looked at before
looking at the arguments)? I don't think this is necessarily an
unsolvable problem, but it must be solved to proceed.

The deal with object dispatch is that what argument provides the
namespace for dispatch is explicitly marked with special syntax in the
call. This inhomogeneity may seem inelegant to LISPers but it does
have the great advantage of simplicity in dispatching method calls, in
that one immediately leaves the global namespace and arrives in a much
narrower one contextually relevant to the call.
Michael Walter
2007-02-23 20:20:57 UTC
Permalink
Post by m***@newhall.net
Post by Matt Knox
If I understand it correctly, your point is that if you need the draw
method from gun, picture, blood, and duel modules, that in an
gun.draw args
duel.draw args
...etc.
instead of
(draw_gun gun args)
But it seems that it should be trivial to have, in a CLOS-style generic
function system, 'draw' bound to a function that would look at the type of
(draw gun args)
...
and the only differences would be syntax and the ease with which
autocompletion could be done.
Am I missing something?
No, I think that is correct, but that is not how CLOS-style generic
OOP works, the original subject of criticism. [...]
In what way is using dynamic dispatch not how CLOS-style generic OOP
works? I mean, this seems to be pretty much what defgeneric/defmethod
are for.

Regards,
Michael
Matt Knox
2007-02-23 20:38:48 UTC
Permalink
Post by Tom Locke
Post by m***@newhall.net
Post by Matt Knox
If I understand it correctly, your point is that if you need the draw
method from gun, picture, blood, and duel modules, that in an
gun.draw args
duel.draw args
...etc.
instead of
(draw_gun gun args)
But it seems that it should be trivial to have, in a CLOS-style
generic
Post by m***@newhall.net
Post by Matt Knox
function system, 'draw' bound to a function that would look at the
type of
Post by m***@newhall.net
Post by Matt Knox
its first argument and dispatch to the appropriate GF. Then you'd
(draw gun args)
...
and the only differences would be syntax and the ease with which
autocompletion could be done.
Am I missing something?
No, I think that is correct, but that is not how CLOS-style generic
OOP works, the original subject of criticism. [...]
In what way is using dynamic dispatch not how CLOS-style generic OOP
works? I mean, this seems to be pretty much what defgeneric/defmethod
are for.
Regards,
Michael
The difference is that if you define 3 different GFs (not different methods
on a single gf, but 3 different functions) in different source files, each
with the same name, CLOS does not automatically smash them together into a
sngle, more generic function. That would be necessary to make

(draw gun )
(draw picture)

work the same way as
gun.draw
picture.draw
--
(def (eval e l d c)
(if (atom? e)
((ahandler (type e)) e l d c)
(eval (car e) l d
(fun (x)
(evapp x (cdr e) l d c)))))
Michael Walter
2007-02-23 21:02:30 UTC
Permalink
Post by Tom Locke
Post by Michael Walter
Post by m***@newhall.net
Post by Matt Knox
If I understand it correctly, your point is that if you need the draw
method from gun, picture, blood, and duel modules, that in an
gun.draw args
duel.draw args
...etc.
instead of
(draw_gun gun args)
But it seems that it should be trivial to have, in a CLOS-style
generic
Post by Michael Walter
Post by m***@newhall.net
Post by Matt Knox
function system, 'draw' bound to a function that would look at the
type of
Post by Michael Walter
Post by m***@newhall.net
Post by Matt Knox
its first argument and dispatch to the appropriate GF. Then you'd
(draw gun args)
...
and the only differences would be syntax and the ease with which
autocompletion could be done.
Am I missing something?
No, I think that is correct, but that is not how CLOS-style generic
OOP works, the original subject of criticism. [...]
In what way is using dynamic dispatch not how CLOS-style generic OOP
works? I mean, this seems to be pretty much what defgeneric/defmethod
are for.
Regards,
Michael
The difference is that if you define 3 different GFs (not different methods
on a single gf, but 3 different functions) in different source files, each
with the same name, CLOS does not automatically smash them together into a
sngle, more generic function.
But why don't you define 3 different methods of the same generic function?

Regards,
Michael
Matt Knox
2007-02-23 21:25:50 UTC
Permalink
Post by Tom Locke
Post by Tom Locke
Post by Michael Walter
Post by m***@newhall.net
Post by Matt Knox
If I understand it correctly, your point is that if you need the
draw
Post by Tom Locke
Post by Michael Walter
Post by m***@newhall.net
Post by Matt Knox
method from gun, picture, blood, and duel modules, that in an
gun.draw args
duel.draw args
...etc.
instead of
(draw_gun gun args)
But it seems that it should be trivial to have, in a CLOS-style
generic
Post by Michael Walter
Post by m***@newhall.net
Post by Matt Knox
function system, 'draw' bound to a function that would look at the
type of
Post by Michael Walter
Post by m***@newhall.net
Post by Matt Knox
its first argument and dispatch to the appropriate GF. Then you'd
(draw gun args)
...
and the only differences would be syntax and the ease with which
autocompletion could be done.
Am I missing something?
No, I think that is correct, but that is not how CLOS-style
generic
Post by Tom Locke
Post by Michael Walter
Post by m***@newhall.net
OOP works, the original subject of criticism. [...]
In what way is using dynamic dispatch not how CLOS-style generic OOP
works? I mean, this seems to be pretty much what defgeneric/defmethod
are for.
Regards,
Michael
The difference is that if you define 3 different GFs (not different
methods
Post by Tom Locke
on a single gf, but 3 different functions) in different source files,
each
Post by Tom Locke
with the same name, CLOS does not automatically smash them together into
a
Post by Tom Locke
sngle, more generic function.
But why don't you define 3 different methods of the same generic function?
Regards,
Michael
Because you might not have written all three modules.

It shouldn't be hard to make the package system create a dispatch function
when it detected function name collisions. Then, as long as the argument
types encode enough information to decide which method to call, you're
fine.
--
(def (eval e l d c)
(if (atom? e)
((ahandler (type e)) e l d c)
(eval (car e) l d
(fun (x)
(evapp x (cdr e) l d c)))))
Michael Walter
2007-02-23 21:52:40 UTC
Permalink
Post by Matt Knox
It shouldn't be hard to make the package system create a dispatch function
when it detected function name collisions. Then, as long as the argument
types encode enough information to decide which method to call, you're fine.
Right, this is pretty much what happens when you use defmethod and
load different source files. So we're really talking about module
system deficiencies here, I suppose.

Regards,
Michael
m***@newhall.net
2007-02-24 00:27:03 UTC
Permalink
Post by Michael Walter
Post by Matt Knox
It shouldn't be hard to make the package system create a dispatch function
when it detected function name collisions. Then, as long as the argument
types encode enough information to decide which method to call, you're fine.
Right, this is pretty much what happens when you use defmethod and
load different source files. So we're really talking about module
system deficiencies here, I suppose.
No. Object1.foo and object2.foo select two entirely unentangled
entities with object dispatch, if object1 and object2 are unrelated.
Smashing *unrelated* methods together that happen to have the same
name into a single entity is deeply wrong, conceptually, and bound to
cause real problems in the real world as well. That CLOS does not do
that is not a shortcoming but a feature.

Generic dispatch forces a collision to occur and *create* a
relationship where the programmer(s) intended none. The user must
then explicitly disentangle this unwanted fender-bender. This may be
a sign that generic dispatch is simply inadequate in the fullness of
time and design space. A module system can alleviate this to a
degree, but perhaps object dispatch really is better. Object dispatch
is not antithetical to functional programming or generic functions, so
I don't think one can necessarily make the argument that generic
dispatch is necessary in a LISPy OO system, as has been suggested.
Joe Marshall
2007-02-24 03:16:55 UTC
Permalink
Post by m***@newhall.net
No. Object1.foo and object2.foo select two entirely unentangled
entities with object dispatch, if object1 and object2 are unrelated.
Smashing *unrelated* methods together that happen to have the same
name into a single entity is deeply wrong, conceptually, and bound to
cause real problems in the real world as well.
Nonsense! We are selecting a function based on two data: the runtime type
of the value of Object and the literal name `foo'. The space of denotable
methods is Type * method-name. How this is implemented is irrelevant.
Whether you have a table per type where each method is an entry in the
table, or if you have a table per method indexed by object type makes no
difference.
Post by m***@newhall.net
Generic dispatch forces a collision to occur and *create* a
relationship where the programmer(s) intended none.
Huh? If I have a generic function `foo' and define two completely unrelated
methods that work on two completely unrelated types, there is no way for
the methods to know about or invoke each other (at least not through normal
means).
Post by m***@newhall.net
The user must
then explicitly disentangle this unwanted fender-bender.
What is there to disentangle?
--
~jrm
m***@newhall.net
2007-02-24 03:34:53 UTC
Permalink
Post by Joe Marshall
Post by m***@newhall.net
No. Object1.foo and object2.foo select two entirely unentangled
entities with object dispatch, if object1 and object2 are unrelated.
Smashing *unrelated* methods together that happen to have the same
name into a single entity is deeply wrong, conceptually, and bound to
cause real problems in the real world as well.
Nonsense! We are selecting a function based on two data: the runtime type
of the value of Object and the literal name `foo'. The space of denotable
methods is Type * method-name. How this is implemented is irrelevant.
Whether you have a table per type where each method is an entry in the
table, or if you have a table per method indexed by object type makes no
difference.
Yes, the internal implementation is not relevant, but neither was I
referring to implementation. In CLOS, one defines a generic function
then specific implementations of it for particular types. Both
internally *and* in the user-visible model, there is an entity called
'foo' (the generic) with multiple methods (the specifics). They share
a function signature. The authors of the disparate methods sharing
the name presumably also share an idea of what it should do. Thus
there is a relationship between the objects sharing the generic.
Exactly analogously to the object-centric idea of sharing a base
method among derived classes. But the generic dispatch approach
obviously works a bit differently, name-wise, and therein lies the
rub.

Of course this sharing is exactly what you don't want if the objects
truly are not related and do not want to share a conceptual function
or an actual function signature. Of course this kind of collision is
what the package system was created to resolve, and it does so.

But the same collision wouldn't happen in the first place with object
dispatch.
Matt Knox
2007-02-23 19:22:04 UTC
Permalink
Post by Matt Knox
If I understand it correctly, your point is that if you need the draw
method from gun, picture, blood, and duel modules, that in an
gun.draw args
duel.draw args
...etc.
instead of
(draw_gun gun args)
But it seems that it should be trivial to have, in a CLOS-style generic
function system, 'draw' bound to a function that would look at the type
of
Post by Matt Knox
(draw gun args)
...
and the only differences would be syntax and the ease with which
autocompletion could be done.
Am I missing something?
Multiple inheritance. I could have a gun-widget class which inherits
from both gun and picture. In your scheme, when I say (draw widget
args), which version of draw should execute? If there's any risk of
this sort of subclassing, you want to write (picture:draw widget args)
or (gun:draw widget args) explicitly.
(Fans of the model-view-controller design pattern will look down on
this specific use of MI, of course.)
Jeremy
If your OO system has a full linearization, (like Dylan and unlike CLOS, I
think), then you have an unambiguous criterion, wouldn't you?
--
(def (eval e l d c)
(if (atom? e)
((ahandler (type e)) e l d c)
(eval (car e) l d
(fun (x)
(evapp x (cdr e) l d c)))))
Pascal Costanza
2007-02-24 01:09:47 UTC
Permalink
Post by Matt Knox
If I understand it correctly, your point is that if you need the
draw method from gun, picture, blood, and duel modules, that in an
gun.draw args
duel.draw args
...etc.
instead of
(draw_gun gun args)
But it seems that it should be trivial to have, in a CLOS-style
generic function system, 'draw' bound to a function that would look
at the type of its first argument and dispatch to the appropriate
(draw gun args)
...
and the only differences would be syntax and the ease with which
autocompletion could be done.
Am I missing something?
It could be that the signatures of the two methods are incompatible
(for example, one has less required arguments than the other). Then
you cannot simply add them to the same generic function. (This is
similar to the example of not being able to implement incompatible
interfaces in the same class in Java.)

Maybe it's possible to design a generic function approach in which
this issue doesn't occur, but I wouldn't know how.


Pascal
--
Pascal Costanza, mailto:***@p-cos.net, http://p-cos.net
Vrije Universiteit Brussel, Programming Technology Lab
Pleinlaan 2, B-1050 Brussel, Belgium
Jeremy H. Brown
2007-02-23 19:10:09 UTC
Permalink
Post by Matt Knox
If I understand it correctly, your point is that if you need the draw
method from gun, picture, blood, and duel modules, that in an
gun.draw args
duel.draw args
...etc.
instead of
(draw_gun gun args)
But it seems that it should be trivial to have, in a CLOS-style generic
function system, 'draw' bound to a function that would look at the type of
(draw gun args)
...
and the only differences would be syntax and the ease with which
autocompletion could be done.
Am I missing something?
Multiple inheritance. I could have a gun-widget class which inherits
from both gun and picture. In your scheme, when I say (draw widget
args), which version of draw should execute? If there's any risk of
this sort of subclassing, you want to write (picture:draw widget args)
or (gun:draw widget args) explicitly.

(Fans of the model-view-controller design pattern will look down on
this specific use of MI, of course.)

Jeremy
Pascal Costanza
2007-02-24 01:06:26 UTC
Permalink
Post by Pascal Costanza
Post by Pascal Costanza
Sooner or later, the libraries of some language will grow bigger, and
then sooner or later, you need objects that adhere to incompatible
interfaces (messages that happen to have the same name but are used for
different purposes). You can then either choose to distribute the
functionality across several objects, and get object identity problems
along the way, or you can choose to incorporate proper namespace
management.
This is a weak argument, because that point is 'sooner' with
CLOS-style generics and 'later' with OO namespaces.
Here is the problem. Consider two interfaces in Java:

interface I1 {
int foo(int x);
}

interface I2 {
String foo(int x);
}

class C implements I1, I2 {

public int foo(int x) {return 0;}

public String foo(int x) {return "";}

}

This doesn't compile. There is no way in Java to define a class that
can implement both I1 and I2. If I1 and I2 are from different vendors
and you need to implement both interfaces, you're screwed.

However, if the method signatures are compatible, things can get even
worse:

interface HasBlood {
void draw();
}

interface HasGun {
void draw();
}

class Person implements HasBlood, HasGun {
?!?
}

So the actual issue is not whether you can distinguish between
gun.draw and blood.draw. The issue is whether you know what happens
when you say person.draw - does this mean that the person in going to
draw a gun, or that blood is going to be drawn from that person?

In other words, you need some form of namespace management to be able
to distinguish such cases. My guess is that people typically just
switch to longer names, like person.drawBlood vs. person.drawGun, and
that's exactly the same solution as saying (draw-blood gun) vs. (draw-
gun person). This is, in other words, just a case of "manual"
namespace management.

That you need namespace management and what your actual namespace
management system looks like if you have one is independent of
whether you have OOP based on message sending or on generic functions.

That you rarely encounter naming issues with message sending may just
mean that you intuitively know how to avoid them in the first place.
I find it hard to tell whether I am intuitively avoiding naming
issues or not when I am programming in Common Lisp. So I don't know
how to honestly answer the question whether naming issues occur more
often with generic functions than with message sending. I just can
give the subjective impression that I have never had any serious
issues with this in Common Lisp.

And to paraphrase something that Joe Marshall said elsewhere some
time ago: The only thing I know for sure about 90% of all programs is
that I haven't seen them.
Post by Pascal Costanza
DING! I declare this round goes to object dispatch.
This is juvenile. I don't want to win a fight, and I couldn't care
less whether you are going to use generic functions or not.

To me, generic functions provide a serious increase in expressive
power and make my life a lot easier. Some tedious design patterns and
idioms simply disappear because of them, especially the Visitor
pattern. It's such an improvement wrt programming convenience that I
probably wouldn't mind the naming issue even if it were real.

I have used both OOP systems extensively in the past, there is no
question for me anymore. I can't tell whether it has the potential of
becoming your preferred choice, but I can explain the advantages and
trade offs that I see.


Pascal
--
Pascal Costanza, mailto:***@p-cos.net, http://p-cos.net
Vrije Universiteit Brussel, Programming Technology Lab
Pleinlaan 2, B-1050 Brussel, Belgium
m***@newhall.net
2007-02-24 02:56:13 UTC
Permalink
Post by Pascal Costanza
...
This doesn't compile. There is no way in Java to define a class that
can implement both I1 and I2. If I1 and I2 are from different vendors
and you need to implement both interfaces, you're screwed.
Here is the problem: this example uses name collision as a result of
multiple inheritance, and the complaint at hand wrt CLOS is a problem
that arises even when multiple inheritance is not involved. Clearly,
the latter situation is more serious -- i.e., more likely to be more
common -- hence my claim that the problem arises sooner with generic
dispatch. That an example can be found that shows a problem is not
compelling, it is a question of comparing the problems in the two
systems. In this case its a bit apples and oranges I think. As an
aside, this is not a problem endemic to object dispatch, but Java's
implementation. In C++ and presumably other languages one could
explicitly resolve the conflict within the class definition.
Post by Pascal Costanza
DING! I declare this round goes to object dispatch.
This is juvenile. I don't want to win a fight, and I couldn't care
less whether you are going to use generic functions or not.
No, it's called 'humor'. Via wordplay I was making the serious point
that I see a clear difference between the two systems in this area and
one seems to 'win' over the other, as in the hacker meanings of
'winnage' and 'lossage', etc. It had nothing to do with a conflict
between humans or an attempt to bruise an ego. Such is e-mail.
Pascal Costanza
2007-02-24 14:24:47 UTC
Permalink
Post by m***@newhall.net
Post by Pascal Costanza
...
This doesn't compile. There is no way in Java to define a class that
can implement both I1 and I2. If I1 and I2 are from different vendors
and you need to implement both interfaces, you're screwed.
Here is the problem: this example uses name collision as a
result of
multiple inheritance, and the complaint at hand wrt CLOS is a problem
that arises even when multiple inheritance is not involved. Clearly,
the latter situation is more serious -- i.e., more likely to be more
common -- hence my claim that the problem arises sooner with generic
dispatch.
I find it hard to tell what is more common and what is not. None of
us has any empirical evidence for one position or the other. We are
just exchanging toy examples here. I don't want to make
generalizations based on toy examples.

But here is another one. Assume someone has written the following
code in a hypothetical dynamically typed language:

class ObjectContainer {

objects;

ObjectContainer(objs) {
this.objects = objs;
}

void draw() {
foreach (object: objects) {
object.draw();
}
}
}

Now, someone uses that code as follows:

new ObjectContainer(new List(picture, gun, blood)).draw();

Good idea?
Post by m***@newhall.net
That an example can be found that shows a problem is not
compelling, it is a question of comparing the problems in the two
systems. In this case its a bit apples and oranges I think. As an
aside, this is not a problem endemic to object dispatch, but Java's
implementation. In C++ and presumably other languages one could
explicitly resolve the conflict within the class definition.
Sure, because C++ has a namespace management system that is more
advanced than Java's.

Maybe this got lost in the noise, but in CLOS I do the following:

(defpackage weapons
(:export draw))

(defpackage graphics
(:export draw))

Then I can say in a different piece of source code:

(defpackage mypackage
(:use :weapons))

...

.... draw .... ;; <= this refers to weapons:draw

...


So I can get all the short names whenever I want them.

The only thing that is not possible is this:

(defpackage mypackage
(:use :weapons :graphics))

I find it hard to believe that you want to write code that is at the
same time about two different kinds of concrete concepts. If that is
the case, it is rather exceptional and it is probably a good idea
anyway to be explicit about what you are talking about.


Pascal
--
Pascal Costanza, mailto:***@p-cos.net, http://p-cos.net
Vrije Universiteit Brussel, Programming Technology Lab
Pleinlaan 2, B-1050 Brussel, Belgium
Mike Newhall
2007-02-24 18:20:47 UTC
Permalink
Post by Pascal Costanza
I find it hard to tell what is more common and what is not. None of
us has any empirical evidence for one position or the other. We are
just exchanging toy examples here. I don't want to make
generalizations based on toy examples.
I hold that local namespaces are less likely to cause name collisions than global ones and that that should not be a controversial point.
Post by Pascal Costanza
new ObjectContainer(new List(picture, gun, blood)).draw();
Good idea?
What is the point of this example?
Post by Pascal Costanza
I find it hard to believe that you want to write code that is at the
same time about two different kinds of concrete concepts. If that is
the case, it is rather exceptional and it is probably a good idea
anyway to be explicit about what you are talking about.
Ah, now the purpose of the example is clear. Perhaps unintentionally, this is a straw man argument again. No-one is advocating the point you are arguing.
Pascal Costanza
2007-02-24 16:07:01 UTC
Permalink
Post by Mike Newhall
Post by Pascal Costanza
I find it hard to tell what is more common and what is not. None of
us has any empirical evidence for one position or the other. We are
just exchanging toy examples here. I don't want to make
generalizations based on toy examples.
I hold that local namespaces are less likely to cause name
collisions than global ones and that that should not be a
controversial point.
Post by Pascal Costanza
new ObjectContainer(new List(picture, gun, blood)).draw();
Good idea?
What is the point of this example?
Post by Pascal Costanza
I find it hard to believe that you want to write code that is at the
same time about two different kinds of concrete concepts. If that is
the case, it is rather exceptional and it is probably a good idea
anyway to be explicit about what you are talking about.
Ah, now the purpose of the example is clear. Perhaps
unintentionally, this is a straw man argument again. No-one is
advocating the point you are arguing.
But the differentiation between gun.draw, blood.draw and picture.draw
is the only example we have discussed so far.

?!?


Pascal
--
Pascal Costanza, mailto:***@p-cos.net, http://p-cos.net
Vrije Universiteit Brussel, Programming Technology Lab
Pleinlaan 2, B-1050 Brussel, Belgium
Loading...