next up previous contents
Next: 2.4 Literals Up: 2 Language Reference Previous: 2.2 Expressions   Contents

Subsections

2.3 Methods

METHODS in Slate are basically annotated block closures (documented in [*]), coupled with annotations of the objects' roles that dispatch to them.

2.3.1 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 their 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 slotname 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 value]. 
_@False ifTrue: _ ifFalse: block [block value]. 
 
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 is defined in terms of the first two, since True and False both delegate to Boolean traits.

2.3.2 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 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}.

2.3.3 Lookup Semantics

Message dispatch in Slate is acheived 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. During the dispatch process, more than one method is often 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:

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.

2.3.4 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 simplest type of resend is resend, which finds the next most-applicable method and invokes it with the exact same set of arguments. The result of resend 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.

2.3.5 Type Annotations

Input and local slots' types can be specified statically for performance or documentation reasons, if desired. The special character ``!'' is used in the same manner as the dispatch annotation ``@'', but type-annotations can only occur within a block closure's header. The type system and inference system in Slate is part of the standard library, and so is explained later.

2.3.6 Macro-level Methods

2.3.6.1 The ` special character

Preceding any selector with a back-tick (`) will cause it to be applied to the parsed pre-evaluated form of its arguments. This provides access to syntax-level methods at run-time and compile-time.

Slate's parser produces syntax trees which are trees of objects with various attributes, so there is some difference from the Lisp family of languages in that simple lists are not the limit of the expression's parsed format.

2.3.6.2 Quoting and Unquoting

A few of the macro-methods we have found appropriate already are `quote and `unquote, which pass as their run-time result the syntax-level shifted versions of their expressions.

`quote causes the surrounding expression to use its quoted value as the input for even normal methods.

`unquote results in an inversion of the action of `quote, so it can only be provided within quoted expressions. Lisp macro system users will note that this effectively makes `quote the same as quasi-quotation.

2

2.3.6.3 Labelled Quotation

In experience with Lisp macros, nested quotation is often found necessary. In order to adequately control this, often the quotation prefix symbols have to be combined in non-intuitive ways to produce the correct code. Slate includes, as an alternative, two operations which set a label on a quotation and can unquote within that to the original quotation by means of referencing the label.

Most users need time to develop the understanding of the need for higher-order macros, and this relates to users who employ them. For reference, a Lisp book which covers the subject of higher-order macros better than any other is On Lisp[3]. However, it's also been said that Lisp's notation and the conceptual overhead required to manage the notation in higher-order macros keeps programmers from entering the field, so perhaps this new notation will help.

The operators are expr1 `quote: aLiteral and expr2 `unquote: aLiteral, and in order for this to work syntactically, the labels must be equal in value and must be literals. As well, the unquoting expression has to be a sub-expression of the quotation. The effect is that nesting an expression more deeply does not require altering the quotation operators to compensate, and it does indicate better what the unquoting is intended to do.

2.3.6.4 Evaluation at Compile-time

`evaluate provides compile-time evaluation of arbitrary expressions.

2.3.6.5 Expression Substitution (Not Yet Implemented)

`with:as: is a protocol for transparent substitution of temporary or locally-provided proxies for environment values and other system elements. This should provide an effective correspondent of the functionality of Lisp's "with-" style macros.

2.3.6.6 Defining new Macro-methods

Macros must be dispatched (if at all) upon the traits of expressions' syntactic representation. This introduces a few difficulties, in that some familiarity is needed with the parse node types in order to name them. However, only two things need to be remembered:

  1. The generic syntax node type is Compiler SyntaxNode traits, and this is usually all that is necessary for basic macro-methods.
  2. Syntax node types of various objects and specific expression types can be had by simply quoting them and asking for their traits, although this might be too specific in some cases. For example, 4 `quote traits is suitable for dispatching on Integers, but not Numbers in general, or (3 + 4) `quote traits will help dispatch on binary message-sends, but not all message-sends. Luckily, [] `quote traits works for blocks as well as methods.


next up previous contents
Next: 2.4 Literals Up: 2 Language Reference Previous: 2.2 Expressions   Contents
The Slate Project 2003-07-29