[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: SOS: Simple Object System

From: Clinton Ebadi
Subject: Re: SOS: Simple Object System
Date: Wed, 24 Sep 2008 18:38:30 -0400
User-agent: Gnus/5.11 (Gnus v5.11) Emacs/22.2 (gnu/linux)

"Maciek Godek" <address@hidden> writes:

> 2008/9/24 Clinton Ebadi <address@hidden>:

>> Interestingly enough, CLOS predates C++ and Java ;-)
> No wonder -- lambda calculus is a formal system to express concepts
> and you can express practically anything in it. C was invented to program
> von Neumann machines, and both C++ and Java are derivatives of C.

C predates Common Lisp and any Lisp object system :-) Note that the OO
features of C++ are *not* derived from C but rather from Simula.

> Note however that both these languages were invented to address
> the particular needs of OOP and as such are well suited for that purpose
> (or at least that's what the most people think. CLOS is rarely used
> compared to C++ and Java -- can you explain why?)

Popularity as a measure of quality is a fallacious argument (one that
many people often use). Java's widespread adoption can be explained by
Sun heavily promoting it with their deep pockets, and C++ as being an
extension of C and coming from Bell Labs.

Remember that Lisp was not usable on workstations and personal
computers until the 90s and the Lisp machines of hte 70s and 80s had a
drastically different OS compared to UNIX. As such it has only been in
the last ten years that Common Lisp/Scheme have become usable for
tasks that C++/Java/etc are used for. There are *very* large
systems--ITA's airline ticket finding system is the canonical example
of a thriving Lisp system.

Lisp was traditionally used for things like expert systems, planners,
CAD, etc. The first two are still not done well by either C++ or Java.

>> Separating classes and generic functions makes the language more
>> powerful--now two orthogonal concepts may be combined in arbitrary
>> ways. The classic example of having generic functions are multimethods
>> wherein you can dispatch on the type of every argument of the
>> generic. A simple example of this is the (simplified) present method
>> from CLIM:
>>  (define-generic present (instance view))
>> Which then allows you to do nifty things like:
>>  (define-method (present (instance <image>) (view <text-view>))
>>     (format #t "[~A]" (alt-text-of instance)))
>>  (define-method (present (instance <image>) (view <graphical-view>))
>>     (display the image somehow))
>> etc.
> And what about
> (define-method (present (zone <timezone>) (part-of-year <season>))
> (define-method (present (santa <donor>) (child <acceptor>)))
> ?

What would the timezone method do? Perhaps you meant:

  (define-generic present-time-zone? (part-of-year))

As it doesn't make much sense to supply the answer to the predicate as
the first argument (unless you meant something).

The other example is solvable via namespaces: You can have a clim and
a gift module, and then reference them as
clim:present/gift:present. The guile syntax for doing this is not so
nice now--((@ (clim) present) ...), but it doesn't have to be (and you
can always use a #:renamer with use-modules).

>> Doing this with a single-dispatch system is much less aesthetically
>> pleasing. Note also that multimethods are only one of many advantages
>> to having generic functions--they also enable method combinations and
>> a few other things. Stylistically, they make the OO system integrate
>> cleanly with the rest of the language rather than having its own
>> specialized syntax for method invocation.
> I see your point, but I can't agree with it -- multimethods certainly
> cause ambiguity and make the code harder to read (you can't tell
> which method will be called unless you know the types of all its
> arguments).

The point of OO methods is that you need not concern yourself with
which method is called: every method on a generic has the same
behavior. If it does not then it violates the protocol, and you can
certainly do this in a single dispatch system as well.

>>> But the most important feature of OOP that is missed
>>> in GOOPS (because of global namespace methods) is the lack
>>> of the clean separation of interface and implementation
>>> in the way it's done in java, C# and the like.
>> Actually, CLOS/GOOPS are perhaps the cleanest way to separate
>> interface from implementation. You define a protocol as a set of
>> generic functions and define methods upon them -- with no concern for
>> the actual classes used with the protocol.
> The disadvantage is that (unlike in Java, C# etc.) the "interface" isn't an
> explicit object. I agree that this can be overcome with a proper discipline
> imposed on source code, but the documentation of GOOPS makes me
> think that no such discipline has yet been developed.

This is a bit of an open problem even if the other languages. You can
specify a collective interface in Java (or a mixin class in C++), but
you still cannot specify the required behavior of the protocol except
in an ad hoc fashion. This is a problem that may never be solved (and
is perhaps a sign that OO is not so great).

You could, of course, add packaged protocol interfaces to GOOPS using
the MOP and a few macros:

  (define-protocol-interface NAME
    (define-generic ...) ...)

  (define-protocol-implementation NAME CLASS
    (define-method ...) ...)

Along with some metaclass magic to ensure that the entire protocol is
indeed defined withing the `define-protocol-implementation' block. The
benefits of this, however, are dubious as you still cannot guarantee
anything about the behavior of the implementation in a programmatic

>> In this way you can do implementation sharing via class inheritance or
>> something akin to Java interfaces (if I understand them properly; I
>> refuse to use such a language) and implement the protocol for
>> arbitrary classes without having to arbitrarily force them to inherit
>> from unrelated classes.
> I don't quite get what you mean (I'd need an example).
> BTW Java seems to gain popularity instead of loosing it.
> (And I think that stupidity of mankind and human ignorance
> are not the only reasons)

Say that you have a system wherein objects can move (obviously an
artificially simple example):

  (define-generic move (thing point))

Now say that you have machines, animals, etc. In a Smalltalkesque OO
system you would need to do something akin to:

  (define-class movable-object ()
    (#:methods (move (thing point) ...)
               ...) ...)
  (define-class machine (... movable-object ...)
  (define-class animal (... movable-object ...)

Taxonimically animals and machines are *not* related by their ability
to move, but they can both move. Thus you run into a problem with your
type discipline as a result. In a language with orthogonal generics
and classes you can simply implement the `move' method for anything
that can move without polluting your class heirarchy. This is
especially important if you are doing taxonomic reasoning of any sort.

Now, you may perhaps raise the objection: "But how would I tell which
classes specialized `move'?" In CLOS at least (I am not quite familiar
enough with the GOOPS internals) you can query the generic function
object for a list of classes that specialize any of its arguments
since the MOP guarantees that this information will be maintained.

I hope this clears things up a bit.

"Karen loved animals. Unfortunately the cheetahs betrayed her trust,"
Libot said.

reply via email to

[Prev in Thread] Current Thread [Next in Thread]