[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: