|
|
Dylan is essentially a cleaned-up version of CLOS, an object-oriented programming system built on LISP. The primary advantage of LISP-like systems is that everything in the system, including the language itself, can be modifed by the language. This makes LISP systems incredibly flexible. However the downside has always been the terribly confusing syntax that most LISP systems use, which is so different than most programming languages that it is often ignored for that reason alone. Another issue is that most LISP systems are interpreters, and therefore tend to be slower than compiled programs, which in the computer industry is an undeserved kiss of death.
Dylan attempted to address the performance problem by introducing "natural" limits to the full flexibility of LISP systems, allowing the compiler to clearly understand compilable units. 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.
| Table of contents |
|
2 Classes 3 Functions 4 Extensibility 5 External links |
In most OO languages the concept of class is the 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.
In addition, 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. 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.
Classes in Dylan are still important however. Classes are constructed from "methods" (code) and "slots" (data) in a fashion similar to most OO languages. Dylan also uses multiple inheritance, but the developers spent enough time on the classloader to avoid the problems that continue to make many uninformed programmers believe that multiple inheritance is a "bad idea".
Class definitions in Dylan typically include the definition of the storage only. For instance:
In this example the class "window" is created. The
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
The Dylan analog of a method is the method, but that simplifies the matter too much. In most OO languages the methods are defined as an integral part of the class itself, meaning that if you wish to use a method from another class it must be imported and called in a wrapper in your class.
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 languages. However functions may also be 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:
This definition is similar to those in other languages, and would likely be encapsulated within the
Generic methods come into their own when you consider more "generic" examples. For instance, one common function in most languages is the
This might strike some readers as very odd. The code to handle toString for a window isn't defined in
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
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 constuct, many problems apply to all objects in the system and there's no natural way to handle this. For instance Java is currently attempting to add this sort of system in Aspect-J, but this is proving very difficult and many Java programmers question the need for it in the first place. Suffice it to say, once you have used a language that allows extensions, you'll never want to go back.
A concrete example of this flexibility is what most other languages refer to as aspect oriented programming, where a particular function "cuts across" most of the objects in the system. This functionality cannot be provided in the basic language, leading to a series of add-ons and non-standard versions of the language as they invariably attempt to add it at some later date.Modules vs. namespace
Classes
define class
title holding a string for the title at the top of the window, 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, which tells the compiler that all access to this slot will be via methods named position and position-setter, which can be overriden but most often don't have to be. Note that this implies that all access to slots are via methods, a feature of most dynamic languages.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 don't do it, whereas in Dylan this completely unrelated concept is completely unrelated.Functions
define method turnBlue (w ::
w.color-setter(, or more accurately, color-setter(w, .toString, 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/interface called "toString", thereby removing this code from the definition of the class itself.Extensibility
SpellCheck 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 SpellCheck module is compiled into your program, all strings (and other objects) will get the added functionality.