This is the mail archive of the guile@cygnus.com mailing list for the guile project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

Re: setf.scm


>>>>> "Per" == Per Bothner <bothner@cygnus.com> writes:

``This letter is longer than usual because I lack the time to make it
short.''  -- Pascal

    Per> I have no CLOS experience.  But I won't let that stop me from
    Per> expressing my opinion ...

Oh, dear. :)

    Per> * The method precedence order is arbitrary, and (supposedly)
    Per> "wrong".

You appear to have come by this through hearsay, and that makes it
somewhat difficult for me to address it, but I'll try.  There's only
one thing where the precedence order could really be ``wrong'', and
that works most of the time.

The topological sort that CLOS does degrades nicely into the
traditional precedence order in the case of single inheritance or
single dispatch.  The topological sort even works fairly well for most
method calls.  It occasionally falls down in borderline cases
involving semi-large multimethod grids; I can furnish an example if
necessary with three classes but it's somewhat contrived.  Rather than
force you to choose which method to use, CLOS defines a tie breaking
algorithm.

Urs Hölzle once pointed out the problem with this to me, mentioning
that the Self system he used to work on had a similar problem and that
the default tie breaking criteria was so arbitrary that the system
implementors themselves were regularly surprised by its behavior.
They couldn't change to something else because *any* tie breaking
criteria would be similarly arbitrary.  Eventually the tie breaking
stuff was removed completely, replaced with a signalled error; and
there was much rejoicing.  I believe a similar `fix' would be
appropriate for this proposed object system.

    Per> * I'm not convinced that multi-methods are worth it.  They
    Per> are more difficult to implement efficiently, they make it
    Per> more difficult to figure out what is going on, they remove
    Per> the modularity advantage that conventional object-oriented
    Per> languages have (that methods are associated with their
    Per> classes).

Technically, it is not too difficult to ensure that the multimethod
implementation incurs no penalties for single dispatching and is
reasonably fast in the presence of multiple dispatch; PCL manages
this, and I think TICLOS does, too.  There's a small body of papers on
how to reduce multiple dispatch to something like single dispatch that
are usually targeted for CLOS and Cecil implementors: if there's
enough interest, I can probably dig up the papers I have.

Multimethods are not as conceptually difficult as you say you are *if*
you treat them as language elements in their own right and not as bags
on the side of some class.  They are magnificent for navigating
hierarchies and CL's generic functions have many useful properties
that `methods' do not, but if you insist on treating them like they're
methods in Smalltalk (for example, by assuming they have to be
associated with anything) and then try to get away with things that
you can't do in Smalltalk (for example, define multiple dispatch) you
can get burned.

As for your modularity argument, you're still thinking of generic
functions as belonging to some class.  It is no trouble at all for me
to restrict access to generic functions through a package/module
system; given that most module systems are more thorough than CL's
package system and it's a breeze even there, I don't see the problem.
If you change the focus from `do this to this object' to `do this
operation on these things' then it becomes much easier conceptually.

There's nothing really for this.  You can program in a `traditional'
object oriented style in CLOS, and it's not hard, although you lose a
lot; there's no sense in restricting the system for those of us who
want the extra flexibility if there's little or no cost for it.

    Per> * I'm nervous about a "mostly-functional" language whose
    Per> semantics depend so much on global state and side-effects.

What, the MOP?  Or inserting the MOP into Scheme?  I'll grant that
parts of the current MOP aren't terribly functional, but a great deal
of that reflects that it was designed for Common Lisp, which has a
strange position in the whole functional/procedural debates.

I would argue that parts of the dispatching and whatnot would be
somewhat procedural, but this is an artifact of the techniques used to
make it fast and doesn't mean it can't be conceptually pure.

Essentially, the magic that underlies ensure-class is there because of
what CL is, and turning it into a more Schemey protocol isn't
conceptually difficult.  You might have to rethink how slot accessors
work, but it's not *hard*, and because CLOS is so tied to CL a certain
amount of transformation before it'll play nice with Scheme is
inevitable.  Heck, most of the work is done for you; make-instance
*will* create instances of anonymous classes if you gag it with enough
data!

    Per> * I'm concerned that efficient compilation and static
    Per> analysis seem to be difficult.

This I see as a very legitimate criticism.  CLOS was never designed to
be statically analyzed and indeed except in simple circumstances it is
extremely difficult to statically analyze it.  The MOP was careful to
flag some changes as particularly expensive, but it is still somewhat
expensive; a classic features/performance tradeoff.

Now, a few things (largely derived from the Self system, also an
extremely dynamic and generally compiler unfriendly environment) off
the top of my head could help a whole lot: customization[0], done
judiciously, could allow us to inline a lot of MOP calls; a simple
type flow analysis program (again, like in Self) could pick up a lot
of the rest; and heavy inlining could make it quite reasonable when
all is said and done.

However, this is all pretty hairy stuff, and I would question whether
it is under guile's purview.  Certainly we could make it fast enough.
I.e. with a fair amount of effort and some time I could probably make
*a* Scheme with this proposed object model run real fast (like, 2x C
code speeds, maybe better); I'm not sure if I can make *this* Scheme
run that fast, and I'm not sure if it really matters all that much.

One caveat with all this: that you *can* implement CLOS well doesn't
mean that you have to.  CMUCL, for example, still relies on PCL last I
checked, and PCL is somewhat portable but dog slow even in the case of
single dispatching.

    Per> 	--Per Bothner Cygnus Solutions bothner@cygnus.com
    Per> http://www.cygnus.com/~bothner

[0] Customization in Self involves recompiling methods from a
superclass for each subclass so that the method benefits from exact
knowledge about the type of `self' (analogous to `this' in C++ code).
This allows us to do much inlining of methods to self, and various
other optimization steps allow methods to be inlined almost into
oblivion; <int> max: <int> can, I believe, be reduced to two branches
and one return while still allowing <int> max: <float> to work.

To make this less space explosive we only customize methods (and
indeed, only compile methods) when they are called for the first time.
In a sense, Self had a better JIT than most implementations for Java
do now *before* Java came on the scene.  I believe the new HotSpot
stuff *finally* is getting around to incorporating this.

Two steps forward, one step back.
-- 
Graham Hughes <ghughes@cs.ucsb.edu>
PGP Fingerprint: 36 15 AD 83 6D 2F D8 DE  EC 87 86 8A A2 79 E7 E6