Dylan programming language
From Free net encyclopedia
The Dylan programming language (pronounced Template:IPA, like the name) is functional, object-oriented, reflective and dynamic. It was created in the early 1990's by a group led by Apple Computer.
Dylan is essentially a cleaned-up and simplified version of CLOS, an object-oriented programming system built on Common Lisp. In Dylan, almost all entities (including primitive data types, methods, and classes) are first-class objects. Programs can be written on a continuum from fully dynamically typed to fully statically typed, allowing for both rapid prototyping and later optimization. Dylan supports multiple inheritance, polymorphism, multiple dispatch, keyword arguments, object introspection, macros, and many other advanced features.
Dylan's main design goal is to be a dynamic language well-suited for developing commercial software. Dylan attempts to address the performance problem by introducing "natural" limits to the full flexibility of Lisp systems, allowing the compiler to clearly understand compilable units (i.e., libraries). Early versions of Dylan were otherwise similar to existing CLOS systems, but developer feedback in the 1993 era forced them to send the product back into engineering and produce a clearer syntax as well.
Contents |
History
Dylan was created in the early 1990's by a group led by Apple Computer. At one point in its development it was intended for use with Apple's Newton computer, but the Dylan implementation did not reach sufficient maturity in time, and Newton instead used a combination of C and the NewtonScript developed by Walter Smith. Apple ended their Dylan development effort in 1995, though they made a "technology release" version available ("Apple Dylan TR1") that included an advanced IDE.
Two other groups contributed to the design of the language and developed implementations: Harlequin released a commercial IDE for Microsoft Windows and Carnegie Mellon University released an open source compiler for Unix systems. Both of these implementations are now open source and maintained by a group of volunteers, the Gwydion Maintainers.
The Dylan language was code-named Ralph. James Joaquin chose the name Dylan for "DYnamic LANguage."
Syntax
At first, Dylan used Lisp syntax, which is based on s-expressions:
(bind ((radius 5) (circumference (* 2 $pi radius))) (if (> circumference 42) (format-out "Hello big circle! c is %=" circumference) (format-out "Hello circle! c is %=" circumference)))
The language was then changed to use an Algol-style syntax, designed by Mike Kahl, which would be more familiar to C programmers:
begin let radius = 5; let circumference = 2 * $pi * radius; if (circumference > 42) format-out("Hello, big circle! c = %=", circumference); else format-out("Hello, circle! c is %=", circumference); end if end
Similar to other functional programming languages, the last evaluation in a function is its return value. This means that the following code is a valid function, returning one of two possible values to the caller of the function:
define method a_number(isTen :: <string>) if (isTen = "10") 10; else 11; end if; end method;
Modules vs. namespace
In most OO languages the concept of class is the primary encapsulation system; the language is generally thought of as "a way to make classes". Modern OO languages often also include a higher level construct known as the namespace in order to collect related classes together. In addition the namespace/class system in most languages defines a single unit that must be used as a whole, if you want to use the String.concat function, you must import and compile against all of String, or the namespace that includes it.
In Dylan the concepts of compile-unit and import-unit are separated, and classes have nothing specifically to do with either. A module defines items that should be compiled and handled together, while an interface defines the namespace. Classes can be placed together in modules, or cut across them, as the programmer wishes. Often the complete definition for a class does not exist in a single module, but is spread across several that are optionally collected together. Different programs can have different definitions of the same class, including only what they need.
What's the difference? Well consider an add-on library for regex support on String. Under traditional languages in order for the functionality to be included in strings, the functionality has to be added to the String namespace itself. As soon as you do this, the String class becomes larger, and people who don't need to use regex still have to "pay" for it in increased library size. For this reason these sorts of add-ons are typically placed in their own namespaces and objects. The downside to this approach is that the new functionality is no longer a part of string; instead, it is isolated in its own set of functions that have to be called separately. Instead of the clean myString.parseWith(myPattern)
syntax that follows classical OO concepts, you are forced to use something like myPattern.parseString(myString)
, which effectively reverses the natural ordering.
In addition, under Dylan many interfaces can be defined for the same code, for instance the String.concat could be placed in both the String interface, and the "concat" interface which collects together all of the different concatenation functions from various classes. This is more commonly used in math libraries, where functions tend to be applicable to widely differing object types.
A more practical use of the interface construct is to build public and private versions of a module, something that other languages include as a "bolt on" feature that invariably causes problems and adds syntax. Under Dylan the programmer can simply place every function call in the "Private" or "Development" interface, and collect up publicly accessible functions in "Public". Under Java or [[C++]] the visibility of an object is defined in the code itself, meaning that to support a similar change the programmer would be forced to re-write the definitions completely, and could not have two versions at the same time.
Classes
Classes in Dylan describe "slots" (data members, fields, ivars, etc.) of objects in a fashion similar to most OO languages. All access to slots are via methods, a feature of most dynamic languages. Default getter and setter methods are automatically generated based on the slot names. In contrast with most other OO languages, other methods applicable to the class are often defined outside of the class, and thus class definitions in Dylan typically include the definition of the storage only. For instance:
define class <window> (<view>) slot title :: <string> = "untitled", init-keyword: title:; slot position :: <point>, required-init-keyword: position:; end class;
In this example the class "<window>
" is defined. The <class name> syntax is convention only, to make the class names stand out—the angle brackets are merely part of the class name. In comparison, in some languages the convention is to capitalize the first letter of the class name or to prefix the name with a "C" or "T" (for example). <window>
inherits from a single class, <view>
, and contains two slots, title
holding a string for the window title, and position
holding an X-Y point for the upper corner of the window. In this particular example the title has been given a default value, while the position has not. The optional "init-keyword" syntax allows the programmer to specify the initial value of the slot when instantiating an object of the class.
In languages such as C++ or Java, the class would also define its interface. In this case the definition above has no explicit instructions, so in both languages access to the slots and methods is considered protected
, meaning they can be used only by subclasses. In order to allow unrelated code to use the window instances, they would have to be declared public
.
In Dylan these sorts of visibility rules are not considered part of the code itself, but of the module/interface system. This adds considerable flexibility. For instance, one interface used during early development could declare everything public, whereas one used in testing and deployment could limit this. With C++ or Java these changes would require changes to the source code itself, so people won't do it, whereas in Dylan this is a completely unrelated concept.
Although this example does not use it, Dylan also supports multiple inheritance.
Methods and generic functions
In Dylan, methods are not intrinsically associated with any particular class; methods can be thought of as existing outside of classes. Like CLOS, Dylan is based on multimethods, where the specific method to be called is chosen based upon the types of all its arguments. The method does not have to be known at compile time, the understanding being that the required functionality may be available or may not, based on the user's preferences.
Under Java the same methods would be isolated in a particular class. In order to use that functionality the programmer is forced to import that class and refer to it explicitly in order to call the method. If that class is not available, or unknown at compile time, the application simply won't compile.
In Dylan, code is isolated from storage in functions. Many classes have methods that call their own functions, thereby looking and feeling like most other OO languages. However code may also be located in generic functions, meaning they are not attached to a particular class, and can be called natively by anyone. Linking a particular generic function to a method in a class is accomplished this way:
define method turn-blue (w :: <window>) w.color := $blue; end method;
This definition is similar to those in other languages, and would likely be encapsulated within the <window>
class. Note the := setter call, which is syntactic sugar for color-setter($blue, w)
.
The utility of generic methods comes into its own when you consider more "generic" examples. For instance, one common function in most languages is the to-string
, which returns some human-readable form for the object. For instance, a window might return its title and its position in parens, while a string would return itself. In Dylan these methods could all be collected into a single module called "to-string
", thereby removing this code from the definition of the class itself. If a particular object did not support a to-string
, it could be easily added in the to-string
module.
Extensibility
This whole concept might strike some readers as very odd. The code to handle to-string
for a window isn't defined in <window>
? This might not make any sense until you consider how Dylan handles the call to to-string
. In most languages when the program is compiled the to-string
for <window>
is looked up and replaced with a pointer (more or less) to the method. In Dylan this occurs when the program is first run; the runtime builds a table of method-name/parameters details and looks up methods dynamically via this table. That means that a function for a particular method can be located anywhere, not just in the compile-time unit. In the end the programmer is given considerable flexibility in terms of where to place their code, collecting it along class lines where appropriate, and functional lines where it's not.
The implication here is that a programmer can add functionality to existing classes by defining functions in a separate file. For instance, you might wish to add spell checking to all <string>
s, which in most languages would require access to the source code of the string class—and such basic classes are rarely given out in source form. In Dylan (and other "extensible languages") the spell checking method could be added in the spell-check
module, defining all of the classes on which it can be applied via the define method
construct. In this case the actual functionality might be defined in a single generic function, which takes a string and returns the errors. When the spell-check
module is compiled into your program, all strings (and other objects) will get the added functionality.
This still might not sound all that obvious, but in fact it is a common problem faced by almost all OO languages; not everything fits into a class construct, many problems apply to all objects in the system and there's no natural way to handle this.
Apple Dylan
Apple Dylan was code-named Leibniz, after the other inventor of the calculus. (It was originally developed as the toolbox and application language the Apple Newton.)
Development Environment
The user interface portion of the Apple Dylan development environment (not counting the compiler, linker, and runtime libraries) was code-named Hula. It was a cross-platform development environment inspired by Smalltalk, Macintosh Common Lisp, and Think C. It contained:
- "Binder": a tool for configuring browsers
- A source database
- A definition database
- Incremental compilation
- A cross-platform source debugger
- Profiling tools
- An interface builder
The basic browser window in Hula was Binder. A Binder window consisted of a number of linked panes. Each pane could have an input, an aspect, and a presentation style. A pane's input was another pane; the pane would display information that pertained to the selection in its input pane. Aspects were properties of the input, such as source code, contents, callers, readers, writers, references, or compilation warnings. This information could be presented in an outline, or a graph. Inputs, aspects, and styles could be used to construct replicas of the Smalltalk source browser, or static call graphs, or ad-hoc displays such as the callers of readers of variables that the selected function writes. All views were live: recompiling a function would update any displays that included its who-calls information, for example.
The outline view included a series of indicators that displayed whether a source item was unsaved, uncompiled, or had warnings.
User Interface Framework
Apple Dylan shipped with a user interface framework that was written in Dylan by Mike Lockwood. The framework was tightly integrated with a WYSIWYG interface builder code-named Meccano, written by Robin Mair. If you linked the interface builder into your application, you could switch between Edit and Run mode while the application was running. Decorators that controlled alignment and border decoration were represented as graphical objects, that could be dragged onto a user interface object to modify its behavior or appearance.
Implementation
Apple Dylan was implemented in Macintosh Common Lisp (MCL). The Apple Cambridge office started life as an acquisition of Coral Software, the developers of Macintosh Common Lisp.
When Dylan was retargetted from the ARM to the desktop, the back end was modified to use APPLEX, a portable assembler designed by Wayne Loufborrow's team in Apple Cupertino.
Developers
Compiler and Runtime:
- David Moon
- Kim Barrett
- Bob Cassels
- Gail Zacharias
- Glen Burke
- John Hotchkiss
- Kálmán Réti
- Mark Preece
- Jeff Piazza
- Steve Strassmann
- Derek White
Language definition:
- Andrew Shallit
- Orca Starbuck
(as well as everyone else)
Hula:
- Oliver Steele
- Paige Parsons
- Bill St. Clair
- Jeremy Jones
- John Hotchkiss
- Neil Mayle
- Steve Strassmann
MCL:
- Alice Hartley
- Bill St. Clair
- Gary Byers
(These lists are incomplete.)
See also
External links
- Getting Started with Dylan
- Gwydion Dylan - host of two optimizing Dylan compilers targeting Unix/linux, Mac OS X, and Microsoft Windows
- The Marlais Dylan Interpreter
- DylanSource.com - An excellent compilation of available Dylan tools and learning resources
- Dylan Programming Language
- Dylan Language Wiki
- Open Directory Project: Dylande:Dylan (Programmiersprache)