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

Re: s-exprs + prototypes

On 2003.06.22 23:28 Michael Vanier wrote:
> I'm not sure what you mean by a "lazily-evaluated dynamic type system".
> Care to elaborate?

There's probably a more accepted term for it... Let's say object A is
of type B.  Normally, in a dynamically-typed language, you can only
make such a statement at runtime.  But what if your language lets you
put off binding the definition of B until runtime?  Then you can say,
at compile time, that A is of type B without statically typing it,
because the exact meaning of B is up in the air.

For example, imagine a type called Color, characterized by levels of
red, green and blue, and a subtype of Color called Purple, defined as
a Color with positive levels of red and blue and zero green.  In a
statically typed language, when you say Purple is a Color you are connecting 
Purple with a particular implementation of Color.  But if
Purple is evaluated lazily, why can't its supertype be determined
lazily as well?  Maybe in a particular context Color has to have a
fourth value, an alpha channel.  Wouldn't it be convenient in such a
case to be able to redefine Color without redefining Purple?  If
a Purple can avoid having to know anything about its supertype until
runtime, such flexibility is possible.

This is really just a variation on a prototype-based object system (types
are defined by objects) and interfaces/aspects (types without the regular
OO baggage).

> Some of the object systems I've seen, such as CLOS-like object systems
> supporting multimethods, have to do quite a bit of work to find the method
> that corresponds to a given selector.  They have to consider the set of
> argument types, look for a method that corresponds to those types, and if
> it isn't found, look for the most specific method that is compatible with
> those types (using the inheritance relationships to help with the
> dispatching, BTW).  That seems to me to be a lot more intricate than just a
> simple scoping problem.  Now, not all object systems work this way.  What
> exactly are you proposing?

I'm not saying all the work can be eliminated, but certain significant
simplifications are possible.  One simplification is to move multimethods
into the type system, i.e. multiple, selective inheritance in place of
traditional multimethods.  For example, a Color type could have several
supertypes -- RedGreenBlue, RedGreenBlueAlpha, HueSaturationValue,
BenjaminMoorePaintNumber, etc. -- and could inherit from one or another of
these types depending on how it is constructed.  
This approach inverts traditional inheritance relationships -- the
subclass is general while the superclasses represent more specific cases.
But it represents a useful abstraction, the factoring of related aspects
of multiple types into a common interface, which is precisely the purpose
of multimethods.

Another simplification is monolithic overloading, which is the requirement
that all versions of a function in a given lexical scope share the same
body.  In such a case the dispatch mechanism is supplied by the programmer
rather than the language and can therefore be as simple or as complex as
desired.  All calls to a method reach the same body of code, but the
name, number and type of parameters passed to the method will vary
depending on how the method is called.  You still need a behind-the-scenes
mechanism for selecting the parameter list that best matches the
arguments, but this task is separated from the job of finding the
method in the first place, which depends only on the method name and the
current scope.  It's more work for the programmer, of course, but
generally not a lot of work, because in a typical overloaded method the
dispatching logic can be extremely simple -- it's having to handle the
general cases that makes most dispatch mechanisms complicated.

But the most important simplification in the FOOP paradigm is the idea
that atomic functions and objects are the same thing.  There are several
consequences of this, all of which tend towards simplification:

    -- first-class functions (only one namespace for functions and objects)

    -- "return type" and "supertype" are the same thing

    -- objects inherit function definitions rather than state

    -- objects encapsulate functions, not data, so type binding and
       inheritance can occur lazily (as explained above)

One final point -- this approach has actually been implemented (largely),
and whether or not it satisfies purists of either the FP or OO variety,
it actually works.


Michael St. Hippolyte