Next: 2.4 Type Annotations
Up: 2 Language Reference
Previous: 2.2 Expressions
  Contents
Subsections
METHODS in Slate are basically annotated
code blocks (documented in
), coupled with
annotations of the objects' roles that dispatch to them.
Method definition syntax is handled relatively separately from normal
precedence and grammar. It essentially revolves around the use of
the reserved character ``@''. If any identifier in a
message-send argument position is found to contain the character,
the rest of the same send is examined for other instances of the symbol,
and the whole send-expression is treated as a template. The parser
treats the expression or identifier to the right of the @
characters as dispatch targets for the method's argument positions;
the actual objects returned by the expressions are annotated with
a role for their positions.
After the message-send template, there is expected a block expression
of some kind, whether a literal or an existing block. Whichever is
specified, the parser creates a new block out of it with adjustments
so that the identifiers in the dispatching message-send become input
slots in the closure. The block should be the final expression encountered
before the next stop (a period).
There is a further allowance that an input slot-name specifier may
be solely an underscore (but not an underscore followed by anything
else), in which case the argument to the method at that position is
not passed in to the block closure.
This syntax is much simpler to recognize and create than to explain.
For example, the following are a series of message definitions adding
to boolean control of evaluation:
-
- _@True ifTrue: block ifFalse: _ [block do].
_@False ifTrue: _ ifFalse: block [block do].
bool@(Boolean traits) ifTrue: block
"Some sugaring for ifTrue:ifFalse:."
[
bool ifTrue: block ifFalse: []
].
The first two represent good uses of dispatching on a particular individual
object (dispatching the ignored symbol ``_'' to True
and False, respectively) as well as the syntax for disregarding
its value. Within their blocks, block refers to the named
argument to the method. What's hidden is that the block given as the
code is re-written to include those arguments as inputs in the header.
The latter method appears to have a slightly-different syntax, but
this is an illusion: the parentheses are just surrounding a Slate
expression which evaluates to an object, much as True and
False evaluate to particular objects; really, any Slate expression
can be placed there, assuming that the result of it is what is wanted
for dispatch. As a side note, this last method is defined in terms
of the first two and is shared, since True and False
both delegate to Boolean traits (the object carrying the
common behavior of the boolean objects).
The specialized syntax using the ``@'' special has an
equivalent in regular Slate syntax which is often useful for generating
new methods dynamically in a non-ambiguous way. This is a reflective
call on the interpreter to compile a method using a certain symbolic
name and a sequence of objects that are used for dispatching targets.
For example:
-
- [| :x :y | 5] asMethod: #+ on: {2. 2}.
and
-
- _@2 + _@2 [5].
are equivalent (while not recommendable) expressions. This raises
the question of a place-filler for an argument position which is not
dispatched. In that case, Slate provides a unique primitive NoRole
for this purpose, which provides an analogous role to Nil:
NoRole cannot be dispatched upon. Essentially, this means
that the following method definition:
-
- c@(Set traits) keyAt: index
[
c array at: index
].
is semantically equivalent to:
-
- c@(Set traits) keyAt: index@NoRole
[
c array at: index
].
and furthermore to:
-
- [| :c :index | c array at: index] asMethod: #keyAt:
on: {Set traits. NoRole}.
Message dispatch in Slate is achieved by consulting all of the arguments
to that message, and considering what roles they have pertaining to
that message name and their position within the message-send. Slate's
dispatch semantics are termed ``multiple dispatch'' to distinguish
from ``single dispatch'' which is typical of most languages based
on objects and messages. Whereas most languages designate on object
as the receiver of a message, Slate considers all objects involved
cooperating participants. During the dispatch process, more than one
method can be discovered as a potential candidate. The most specific
candidate is chosen as soon as its place in the order is determined.
The algorithm achieves a full ordering of arguments: the specificity
of the first argument counts more than the second, the second more
than the third, and so on. However, where normal multiple dispatch
uses the most specific supertype to determine specificity (or rather,
the most specific parameter type of which the argument is a subtype),
specificity is instead interpreted as DISTANCE in the directed
graph of delegations, starting from the argument as the root.
The DISTANCE notion has the following properties:
- It is determined by a depth-first traversal over the delegate slots,
considering most-recently-added delegates before previously-added
ones.
- Delegations that lead to cycles are not traversed.
- Repeated finds of a same method do not alter the distance value for
it; the first one found is retained.
- The closer (smaller) the DISTANCE of the role to the argument,
the more specific it is.
The resulting dispatched method satisfies the property that: for any
of the arguments, we can find the method on some role reachable by
traversing delegations, and that is the closest such method we can
find (where former arguments count as being ``closer'' than any
subsequent arguments), where NoRole behaves like an ``omega
distance'', as far away as possible.
The mechanism in Slate for specifying optional input arguments uses
optional keywords for a syntax. They can be added to a method
definition, a message-send, or a block header equally. Also, optional
keywords can apply to unary, binary, and keyword message syntaxes
equally. However, optional arguments cannot affect dispatch, and when
not provided will start with a Nil value.
Method definitions may be annotated with optionals that they support
by extending the signature with keyword-localname pairs as ``&keywordName: argName''.
This compiles the method to support argName as a local, with
optional input.
An optional keyword argument is passed to a method by forming keyword-value
pairs as ``&keywordName: someValue''. Following keywords
that have the &-prefix will be collected into the same message-send.
A following non-optional keyword will be treated as beginning a new
surrounding message-send, but in general, optional keywords raise
the precedence of the basis message signature to a keyword level,
instead of just unary or binary.
A block can declare optional input keywords in its block header, using
``&argName'' as an input variable declaration, called
with the normal convention, whenever the block is invoked with a message-send.
Because Slate's methods are not centered around any particular argument,
the resending of messages is formulated in terms of giving the method
activation itself a message. The following are the various primitive
protocols involved in resends:
- resend
- is the simplest form of resending. It acts on the
context to find the next-most-applicable method and invokes it with
the exact same set of arguments. The result of the resend
message is the returned result of that method.
- methodName findOn: argumentArray
- locates the method for
the given symbol name and group of argument objects.
- methodName findOn: argumentArray after: aMethod
- locates
the method following the given one with the same type of arguments
as above.
- methodName sendTo: argumentArray
- is an explicit application
of a method, useful when the symbol name of the method needs to be
provided at run-time.
- sendWith:,
- sendWith:with: and sendWith:with:with:
take one, two, and three arguments respectively as above without creating
an array to pass the arguments in.
- methodName sendTo: argumentArray through: dispatchArray
- is
an extra option to specify a different signature for the method than
that of the actual argument objects.
Also, both sendTo: and sendTo:through: accept an
&optionals: optional keyword which is passed an Array
of the alternating keyword symbols (not the names of the locals: those
are defined per method) and values to use.
The multiple dispatch system has an extended dynamic signature form
which can be used to give a ``subjective'' or ``layered''
customization of the Slate environment. This is an implementation
and slight modification of the Us language features conceived of by
the Self authors[Smith 96].
- Slate dispatch signatures are ``enlarged'' to support two implicit
endpoints: one before the first argument and one after. We refer to
the first role as an ``adviser'' or Layer, and to the
second as a Subject or ``interleaver''. The layer role,
being ``to the left'' of the explicit argument positions, has
higher precedence than any of them; the subject role has a correspondingly
opposite role: it has the lowest precedence of all.
- Two primitive context-handling methods were added to support invoking
code with a different object used for one of these new roles. What
happens is that you execute a block [] seenFrom: someSubject
in order to make all methods defined within dispatched with that object
as the subject, and all methods looked up in that context (or any
other context ``seen from'' that object) used with that subject
in the dispatch.
The effect of combining these two mechanisms is that there is a means
for the user to dynamically (and transparently) extend existing libraries.
The Layer usage has a more ``absolute'' power to override,
since without dispatching on any other arguments, a method defined
in a layer will match a message before any other can. The Subject
usage has a more fine-tuned (or weaker, in another sense) ability
to override, since without any other dispatching, a method defined
with a certain subject will never be called. However, taking an existing
method's signature and defining a customized version with a subject
will allow customizing that specific method without affecting any
other method with that selector.
- Methods defined with a special subject or layer persist with
those objects, since they are just dispatch participants.
- Resending messages works just the same within subjective methods as
in normal methods; the same dispatch mechanism is in effect, so the
ability to combine or extend functionality is available.
- Nesting subjective scopes has a dynamic scoping effect: the
actions taken within have run-time scope instead of corresponding
exactly to how code is lexically defined. This gives the compositional
effect that should be apparent when viewing nested subjective scopes.
- Methods defined in non-subjective contexts have no subject or layer
rather than any ``default'' subject: they are for most purposes,
``objective''.
- Subject
- the type of object which provides an appropriate
handle for subjective interleaving behavior in dynamically overriding
or extending other methods' behaviors.
- Layer
- the type of object which provides an appropriate
handle for subjective layering behavior in dynamically overriding
or extending other methods' behaviors.
- [] seenFrom: aSubject
- executes the contents of the
block with the given Subject dynamically affecting the execution
of the expressions.
- aLayer layering: []
- executes the contents of the
block with the given Layer dynamically affecting the execution
of the expressions.
- [] withoutSubject
- executes the contents of the block
without any subject.
- [] withoutLayers
- executes the contents of the block
without any layer.
Next: 2.4 Type Annotations
Up: 2 Language Reference
Previous: 2.2 Expressions
  Contents
The Slate Project
2004-08-06