This page contains information that is not specific to a certain class or method.
This page contains information that is not specific to a certain class or method. | |
Dylan provides an identity function, as well as a group of equality and magnitude comparison functions that can be extended for user classes. | |
For efficiency, Common Dylan adopts a slightly different table protocol to that described by the DRM. | |
The various -definer macros may be used with a number of adjectives. | |
Gwydion compilers have an abstract class <general-integer> which has two concrete subclasses, <integer> and <extended-integer>. | |
In the language defined by the DRM, the only mechanism available for specializing such methods is to use singleton types. |
Dylan provides an identity function, as well as a group of equality and magnitude comparison functions that can be extended for user classes. The functions ~=, ~==, >, <=, >=, min and max are defined in terms of == or = and <. By extending the behavior of = and <, programs can extend the behavior of the other functions.
For the protocol to work, user-defined methods on = and < must preserve the following properties:
If (a = b), then (b = a).
If (a < b) and (b < c), then (a < c).
If (a = b) and (b = c), then (a = c).
Exactly one of: (a < b), (a = b), (b < a) always holds (on the assumption that these two operations are defined for the objects in question).
For efficiency, Common Dylan adopts a slightly different table protocol to that described by the DRM. Hashing functions take an additional hash-state argument and merge it into the hash-state result. The function merge-hash-codes is replaced by merge-hash-ids because hash states are merged as part of the hashing process. The constant $permanent-hash-state is no longer required; the same effect can be achieved by returning the argument hash-state unchanged as the result hash-state. Finally, object-hash has been altered to use the new protocol.
The various -definer macros may be used with a number of adjectives. Some are defined by the Dylan language, others by Gwydion Dylan.
| thread | The variable’s value is specific to each thread. |
| open | The generic method may be given additional methods. |
| sealed | The generic method may not be given additional methods. |
| abstract | The class may not be instantiated. |
| concrete | The class may be instantiated. |
| free | The class is not restricted to a single inheritance chain. |
| functional | The class only has constant slots. Equality is defined by slot value and object class. |
| open | The class may be subclassed. |
| primary | The class forms a single inheritance chain with other primary classes. |
| sealed | The class may not be subclassed. |
| class | The slot’s storage is shared by all classes and subclasses. |
| constant | The slot’s value may not be changed. |
| each-subclass | The slot’s storage is shared within each subclass. |
| instance | The slot’s storage is unique to each instance of the class. |
| virtual | The slot has no storage. |
| default-inline | Inline the function within a library, at the compiler’s discretion. Never inline a cross-library reference. |
| flushable | The function’s value depends on global state, but it does not change global state. |
| inline | Inline the function wherever the compiler can do so. |
| inline-only | Forces every reference to the function to be inlined. |
| may-inline | Inline the function within or between libraries, at the compiler’s discretion. |
| movable | The function’s value does not depend on global state. The value only depends on the function’s arguments. |
| not-inline | Never inline the function. |
| sideways | ? |
Gwydion compilers have an abstract class <general-integer> which has two concrete subclasses, <integer> and <extended-integer>. <integer>s have a limited range of values, and <integer> arithmetic uses the computer’s underlying integer facilities. <extended-integer>s can take on any value, and are similar to Common Lisp “bignums.” Expressions involving <extended-integer>s produce <extended-integer> results because <extended-integer>s are contagious. If an expression involving only <integer> values would produce a result that does not fit in an <integer>, then the Gwydion compiler will signal an overflow error. You can use the as function to convert back and forth between <integer>s and <extended-integer>s. as signals an error when converting an <extended-integer> to a <integer>, and the value does not fit in a <integer>.
In the language defined by the DRM, the only mechanism available for specializing such methods is to use singleton types. A singleton type specializer used in this way, by definition, gives a method applicable to exactly one type. In particular, such methods are not applicable to subtypes of the type in question. In order to define reusable methods on generic functions like this, we need a type which allows us to express applicability to a type and all its subtypes. This is provided by the subclass function.
For an object O and class Y, the following instance? relationship applies:
| INSTANCE-1 | instance?(O, subclass(Y)) is true if and only if O is a class and O is a subclass of Y. |
For classes X and Y the following subtype? relationships hold (note that a rule applies only when no preceding rule matches):
| SUBTYPE-1 | subtype?(subclass(X), subclass(Y)) is true if and only if X is a subclass of Y. |
| SUBTYPE-2 | subtype?(singleton(X), subclass(Y)) is true if and only if X is a class and X is a subclass of Y. |
| SUBTYPE-3 | subtype?(subclass(X), singleton(Y)) is always false. |
| SUBTYPE-4 | subtype?(subclass(X), Y) where Y is not a subclass type is true if Y is <class> or any proper superclass of <class> (including <object>, any implementation-defined supertypes, and unions involving any of these). There may be other implementation-defined combinations of types X and Y for which this is also true. |
| SUBTYPE-5 | subtype?(X, subclass(Y)) where X is not a subclass type is true if Y is <object> or any proper supertype of <object> and X is a subclass of <class>. |
Note that by subclass relationships SUBTYPE-4 and SUBTYPE-5, we get this correspondence: <class> and subclass(<object>) are type equivalent.
Where the subtype? test has not been sufficient to determine an ordering for a method’s argument position, the following further method-ordering rules apply to cases involving subclass types (note that a rule applies only when no preceding rule matches):
| SPECIFICITY+1 | subclass(X) precedes subclass(Y) when the argument is a class C and X precedes Y in the class precedence list of C. |
| SPECIFICITY+2 | subclass(X) always precedes Y, Y not a subclass type. That is, applicable subclass types precede any other applicable class-describing specializer. |
The constraints implied by sealing come by direct application of sealing rules 1-3 (see page 136 of the DRM) and the following disjointness criteria for subclass types (note that a rule applies only when no preceding rule matches):
| DISJOINTNESS+1 | A subclass type subclass(X) and a type Y are disjoint if Y is disjoint from <class>, or if Y is a subclass of <class> without instance classes that are also subclasses of X. |
| DISJOINTNESS+2 | Two subclass types subclass(X) and subclass(Y) are disjoint if the classes X and Y are disjoint. |
| DISJOINTNESS+3 | A subclass type subclass(X) and a singleton type singleton(O) are disjoint unless O is a class and O is a subclass of X. |
The guiding principle behind the semantics is that, as far as possible, methods on classes called with an instance should behave isomorphically to corresponding methods on corresponding subclass types called with the class of that instance. So, for example, given the heterarchy
<object>
|
<A>
/ \
<B> <C>
\ /
<D>
and methods
method foo (<A>)
method foo (<B>)
method foo (<C>)
method foo (<D>)
method foo-using-type (subclass(<A>))
method foo-using-type (subclass(<B>))
method foo-using-type (subclass(<C>))
method foo-using-type (subclass(<D>))
that for a direct instance D1 of <D>
foo-using-type(<D>)
should behave analogously to
foo(D1)
with respect to method selection.
define class <A> (<object>) end;
define class <B> (<A>) end;
define class <C> (<A>) end;
define class <D> (<B>, <C>) end;
define method make (class :: subclass(<A>), #key)
print("Making an <A>");
next-method();
end method;
define method make (class :: subclass(<B>), #key)
print("Making a <B>");
next-method();
end method;
define method make (class :: subclass(<C>), #key)
print("Making a <C>");
next-method();
end method;
define method make (class :: subclass(<D>), #key)
print("Making a <D>");
next-method();
end method;
? make(<D>);
Making a <D>
Making a <B>
Making a <C>
Making an <A>
{instance of <D>}