next up previous contents index
Next: 2.5 Type Annotations Up: 2 Language Reference Previous: 2.3 Sequencing Expressions   Contents   Index

Subsections

2.4 Methods

METHODS in Slate are basically annotated code blocks (documented in 2.1.1), coupled with annotations of the objects' roles that dispatch to them.

2.4.1 Roles

A relatively unique concept in the language is that objects relate to their methods via an implicit idea called a ROLE. A role is an association between an object and a method that applies to it that is similar to a slot, but not directly a slot as it is in Self. Instead of simply being a storage reference, a role associates the object with a position in the signature of a method. This is a way of stating that the object ``plays a role'' in a certain behavior, reinforcing the idea that behavior arrives through cooperation. Furthermore, the behavior is shared among the cooperators, so methods are not ``owned'' by particular objects; they are not properties.

However, because dispatch can happen on many combinations of arguments, a method name plus the object's position is not sufficient to identify a single method; instead, a group of methods of the same name may be associated via a role with an object at a certain signature position. During method lookup, the appropriate method is found through knowing all of the objects in the signature and knowing which take precedence over others. So a specific role consists of a triple-association between an object, its position in a signature for a method, and the method itself.

When referring to an object's ``roles'' in general, it usually means the collection of these associations as a whole; what relates the object to the behaviors it participates in.

2.4.2 Method Definitions

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 argument named ``_'' 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. Also, the objects given to the dispatch annotation are configured to install this method in appropriate roles; because this is hidden, roles are normally an implicit concept and should not concern the user except as an explanation for dispatch.

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).

2.4.3 Expression-based Definitions

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 evaluator to compile a method using a certain symbolic name and a sequence of objects that are used for dispatching targets. For example:

[| :x :y | Nil] asMethod: #+ on: {True. False}.
and

_@True + _@False [Nil].
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}.


2.4.4 Lookup Semantics

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 looks at each argument and uses the most specific supertype to determine specificity (or rather, the most specific parameter type of which the argument is a subtype), Slate instead interprets specificity as DISTANCE in the directed graph of delegations, starting from the particular argument in question as the root.

The DISTANCE notion has the following properties:

So, Slate's lookup algorithm visits each argument in turn, determining candidate applicable methods as ordered by the DISTANCE notion, and traverses further to look for other possible candidates, unless it rules out the possibility of a more applicable method than a singly-identified one.

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, and NoRole behaves like an ``omega distance'', as far away as possible.

2.4.5 Optional Keyword Arguments

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, can be provided in any order, and when not provided will start with a Nil value, due to their being incidental to the core semantics of the given block or method.


2.4.5.1 In Method Definitions

Method definitions may be annotated with optionals that they support by extending the signature with keyword-localname pairs in arbitrary order as ``&keywordName: argName''. This compiles the method to support argName as a local, with optional input.


2.4.5.2 In Message Sends

An optional keyword argument is passed to a method by forming keyword-value pairs as ``&keywordName: someValue'' after the main selector and arguments as normal. 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. Again, the order of the keyword-value pairs is ignored.


2.4.5.3 In Code Blocks

A block can declare optional input keywords in its block header in any order, using ``&argName'' as an input variable declaration, called with the normal convention (when using the block with do/applyTo:/etc.), whenever the block is invoked with a message-send.

2.4.6 Resending messages or Dispatch-overriding

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 (including optional parameters passed) as an expression. The result of the resend message is the returned result of that method, just like calling any other method.
methodName findOn: argumentArray
locates and answers the method for the given Symbol name and group of argument objects.
methodName findOn: argumentArray after: aMethod
locates and answers 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. It returns the result of the method.
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. It returns the result of the method.
methodName sendTo: argumentArray through: dispatchArray
is an extra option to specify a different signature (the dispatchArray, where the new lookup starts) for the method than that of the actual argument objects. It returns the result of the method.
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.

2.4.7 Subjective Dispatch (currently disabled)

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].

2.4.7.1 Basic mechanisms

  1. Slate dispatch signatures are ``enlarged'' to support two implicit endpoints: one before the first argument and one after the last argument. 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.
  2. 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.

2.4.7.2 Important features

2.4.7.3 The core elements

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 up previous contents index
Next: 2.5 Type Annotations Up: 2 Language Reference Previous: 2.3 Sequencing Expressions   Contents   Index
Brian Rice 2005-11-21