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

Re: Some thoughts on using Dylan



On Fri, Oct 12, 2001 at 01:30:01PM -0400, ron.franke-polz@psd.invensys.com wrote:
> * the Abstract Factory Pattern
> This pattern was used heavily in both the application and the tools.  In
> Java, this was a very formal pattern involving the creation of special
> classes to support any Abstract Factory(s).  In Dylan, this could be
> approached much more casually.  A <string-table> containing the name of
> the "part" to create and a function (either an anonomous method or a
> curried' function) was all that it took to realize this pattern.

Don't forget symbols, either--they're great for this kind of use.  They
look like strings, but they work like numeric IDs.  So you could do
something like this:

  *factory-table*[#"button"]("OK", close-dialog)

or even:

  as(<symbol>, prompt-user())

It's like being able to create enumerations at runtime, or like those
four-character IDs used everywhere in the MacOS.

Symbols are unique.  You can assume that #"foo" == #"foo".
 
> Additionally, patterns like the "Visitor" are implemented in Dylan easily
> enough that you really don't think about this as a formal "pattern".
> They become more of an "idiom" of the language.

Multiple dispatch can work wonders here.

> * Dylan's  module system
> For the most part, newbies can ignore it or use it very casually.  But when
> you need it (i.e. name collisions), darn it's handy.

Dylan's module systems strikes me as a bit heavyweight, actually.  For
Proto, we experimented with a simpler design (and perhaps went a bit
overboard):

  * Each module is a file, and vice versa.
  * Modules can be arranged in directory hierarchies.
  * You import a module by typing '(use foo/bar)' at the top of a source
    file.
  * You export symbols from within the file that defines them.

Like Dylan, the underlying code supports re-exporting, renaming, and all
those other good things, though some of these features remain unimplemented
at the user level.  The module system enforces a DAG, but there's a way to
declare forward cross-module references, at least for the time being.

This seems to retain many of the best features of Dylan's module system,
but without requiring '-library.dylan' files, '.lid' files, 'define
library' forms, 'define module' forms, and all that other stuff which
confuses users.  Just '(use ...)' and '(export ...)', all from within
single source files.

> - Dylan's multi-dispatch system
> When I first started with Dylan, I had been using SmallTalk and
> Objective-C alot.  Dylan's seperation of data-structure and dispatch took
> a little getting used to.  Once I "got it", it now feels much more
> natural and expressive.  Add a clean approach to multiple inheritance and
> things get very powerfull and expressive.

Isn't it a weird feeling?  But after a while, multiple dispatch really
begins to feel like a both simplication and a generalization--at the same
time.

Polymorphism is one of the great wins, but the classic model has two
drawbacks:

  * All polymorphic methods must be declared as part of the class.  This is
    is basically impossible in many enterprise applications, where a single
    library is re-used by dozens of applications, each of which need
    different polymorphic behavior.  Once you start to notice this, you'll
    see it everywhere--in many real-world programs, the class hiearchy is
    relatively stable, but the polymorphic functions change frequently.

  * Languages like C++ support two kinds of polymorphism: single-dispatch,
    which happens at runtime, and operator overloading, which happens at
    compile-time.  Multiple dispatch unifies the two concepts.

Plus, multiple dispatch can be very cheap.  The DAS98 scheme costs:

  Per dispatching argument:
    - One lookup of the object's class
    - One lookup of the class's ID
    - One p-table lookup
    - One addition
  The dispatch itself:
    - One m-table lookup
    - One jump

(The terms "p-table" and "m-table" refer to the pole tables and method
tables in the DAS98 paper.)

They claim this dispatch costs 5P+3 instructions on a SPARC, compared to
the 3 instructions required for regular single dispatch.  It requires
suprisingly small tables, and the algorithms are brilliantly simple.  Even
the dcache behavior is relatively plausible. ;-)

Of course, the compiler is always free to use traditional single-argument,
class-tied dispatch when applicable, and not pay the overhead.

> * Functions as 1st class objects - ask for it by name!

Yum, yum.  Can you use choose, map, curry, compose and conjoin all in one
expression? ;-)

> * Dynamism - keeps expressions simple and clean.

There's a lot to be said for dynamism, and a lot to be said for static
typing.  Dylan takes the middle road, known as "soft typing"--you can have
type declarations when you want them, and leave them out when you don't.

There's a deep wisdom to this approach, I think, and I hope scripting
languages adopt it in the future.

Cheers,
Eric


References: