Footnotes
[1]Since this was written, such implementations are well underway on a variety of platforms.[2]Now Apple Cambridge Engineering.
[3]The term interpreter is used, even though most implementations of Dylan are expected to be compiled. In most cases, an interpreter will be implemented by compiling Dylan expressions and immediately executing the compiled code.
[4]Notable Lisp systems that use generic functions are CLOS (Common Lisp Object System), Oak Lisp, and EuLisp.
[5]A heterarchy--also called a directed acyclic graph (DAG)--is like a hierarchy, except that nodes can have multiple parents. Dylan classes are in a heterarchy because Dylan supports multiple inheritance.
[6]Three notable exceptions to the terminal question mark convention are <, >, and =.
[7]Notable exceptions to the terminal exclamation point convention are := and all the setter variable names.
[8]The term "syntax form" describes both special forms and macros. They are equivalent to macros in other languages.
[9]Except in the special slot reference syntax argument.function, where argument is evaluated first, followed by function.
[10]This is in sharp distinction to C, which equates 0 and the false value, and some dialects of Lisp, which equate the empty-list and the false value.
[11]Note that collection clauses are implemented using forward-iteration-protocol.
[12]Trichotomy also implies antisymmetry [if (a < b), then ~(b < a)] and antireflexivity
[if (a == b), then ~(a < b))]. It also implies commutativity for = [if (a = b), then (b = a)].
[13] The trichotomy rule does not hold for IEEE floating-point comparisons. IEEE requires all comparison operations to return false if one of the operands is a NaN. This means that the generic Dylan equality and magnitude predicates will not be IEEE compliant.
[14] At an implementation level, this will usually mean that the objects are pointers to the same storage or are the same immediate value. An extension is made for built-in number classes and characters. Because these objects are not mutable (i.e., cannot be changed), two with the same value will always be the same (and will thus be indistinguishable to programs).
[15]When methods are called directly, the method special form corresponds to the lambda special form of Lisp and to blocks in Smalltalk.
[16]In practice, an implementation may place a reasonable limit on the number of arguments that may be passed to any function.
[17]This section is similar to the approach taken in Craig Chambers' language Cecil (U. Washington). This approach is different from CLOS in that there is no reference to the lexicographic order of arguments when multimethods are sorted. See the examples for more detail on these differences.
[18]This is a difference from CLOS. Under the CLOS system, the example would work as follows: when superior-being is called on a being of type <vulcan> and a being of type <human>, the best-looking-being is chosen when the <human> is the first argument, and the most-intelligent-being is chosen when the <vulcan> is the first argument.
[19]This section is intended to present the same algorithm as in CLOS.
[20]Another way to think of next-method is that it invokes the method that would have been invoked if the current method did not exist.
[21]A method may be removed from a generic function if it is proved that it will never be called. This will be the case if any of the objects on which the method specializes are garbage collected.
[22]For example, Self, and to some degree, CLOS.
[23]Note that the pointer from a class to its subclasses is through a weak link, so subclasses may be garbage collected if there are no other references to them.
[24]That is, no more than two implementations are required for a function that operates on both keys and elements.
[25]With one exception: comparison operations may not behave in IEEE fashion when performed on NaNs.
[26]The two calling possibilities are described as tail-recursive to ensure that all values returned by the call are returned by the handler. Not returning all the values could interfere with the condition's recovery protocol. A handler that really knows what it is doing could use a non-tail-recursive call, but anything that knows what it's doing in this situation is probably unmodular. Note that a handler might not know the full recovery protocol, because the condition's class might be a subclass of the handler's expected type.
[27]This is necessary to keep restart handlers available to other condition handlers.
[28]In the general case, reflective operations can be used to defeat module encapsulation. For example, a programmer can trace from an instance to its class by calling object-class on the instance, even if the implementor of the class did not export the variable containing the class. This problem can sometimes be solved by the proper use of sealing, which blocks many reflective operations.