Next: 2.2 Expressions
Up: 2 Language Reference
Previous: 2 Language Reference
Contents
Index
Subsections
OBJECTS are fundamental in Slate; everything
in a running Slate system consists of objects. Slate objects consist
of a number of slots and roles: slots are mappings from
symbols to other objects, and roles are a means of organizing
code that can act on the object. Slots themselves are accessed and
updated by a kind of message-send which is not distinguishable from
other message-sends syntactically, but have some important differences.
Objects in Slate are created by cloning existing
objects, rather than instantiating a class. When an object is cloned,
the created object has the same slots and values as the original one.
The new object will also have the access and update methods for those
slots carried over to the new object. Other methods defined on the
object will propagate through an analogue of a slot called a role,
explained in section 2.3 on Methods.
Both control flow and methods are implemented by specialized objects
called blocks, which are code closures.
These code closures contain their own slots and create activation
objects to handle run-time context when invoked. They can also be
stored in slots and sent their own kinds of messages.
A code block is an object
representing an encapsulable context of execution, containing local
variables, input variables, the capability to execute expressions
sequentially, and finally answers a value to its point of invocation.
The default return value for a block is the last expression's value;
an early return can override this.
Blocks have a special syntax for building them up syntactically. Blocks
can specify input slots and local slots in a header between vertical
bars (||), and then a sequence of expressions which comprises
the block's body. Block expressions are delimited by square brackets.
The input syntax allows specification of the slot names desired at
the beginning. For example,
-
- Slate> [| :i j k | j: 4. k: 5. j + k - i].
[]
creates and returns a new block. Within the header, identifiers that
begin with a colon such as :i above are parsed as input slots.
The order in which they are specified is the order that arguments
matching them must be passed in later to evaluate the block. If the
block is evaluated later, it will return the expression after the
final stop (the period) within the brackets, j + k - i.
In this block, i is an input slot, and j and k
are local slots which are assigned to and then
used in a following expression. The order of specifying the mix of
input and local slots does not affect the semantics, but the order
of the input slots directly determines what order arguments need to
be passed to the block to assign them to the correct slots.
Using the term "slot" for local and input variables
is not idle: the block is an actual object with slots for each of
these variables, and accessors defined on them which are even callable
from outside the block, considering it as an object.
In order to invoke a block, the client must
know how many and in what order it takes input arguments. Arguments
are passed in using one of several messages. By evaluating these messages,
the block is immediately evaluated, and the result of the evaluation
is the block's execution result.
Blocks that don't expect any inputs respond to do,
as follows:
-
- Slate> [| a b | a: 4. b: 5. a + b] do.
9
Blocks that take one, two, or three inputs, each have special messages
applyWith:, applyWith:with:,
and applyWith:with:with:
which pass in the inputs in the order they were declared in the block
header. Every block responds properly to applyTo:
however, which takes an array of the input values as its other argument.
-
- Slate> [| :x :y | x quo: y] applyWith: 17 with: 5.
3
Slate> [| :a :b :c | (b raisedTo: 2) - (4 * a * c)]
applyTo: {3. 4. 5}.
-44
If a block is empty, contains an empty body,
or the final expression is terminated with a period,
it returns Nil when evaluated:
-
- Slate> [] do.
Nil
Slate> [| :a :b |] applyTo: {0. 2}.
Nil
Slate> [3. 4.] do.
Nil
Blocks furthermore have the property that, although they are a piece
of code and the values they access may change between defining the
closure and invoking it, the code will ``remember'' what objects
it depends on, regardless of what context it may be passed to as a
slot value. It is called a lexical closure
since it ``closes over'' the environment and variables used in
its definition, the lexical context where it was born. This is critical
for implementing good control structures in Slate, as is explained
later. Basically a block is an activation of its code composed with
an environment that can be saved and invoked (perhaps multiple times)
long after it is created, and always do so in the way that it reads
where it was defined.
Slots
may be mutable or immutable, and explicit slots or delegation (inheritance)
slots. These four possibilities are covered by four primitive methods
defined on all objects.
Slate provides several primitive messages to manage slots:
- object addSlot: slotSymbol
- adds
a slot using the symbol as its name, initialized to Nil.
- object addSlot: slotSymbol valued: val
- adds
a slot under the given name and initializes its value to the given
one.
- object removeSlot: slotSymbol
- removes
the slot with the given name on the object directly and returns whatever
value it had.
- object addDelegate: slotSymbol
- and
object addDelegate: slotSymbol valued: val add
a delegation slot, and initialize it, respectively. It is recommended
to use the latter since delegation to Nil is unsafe.
Each
of the these has a variant which does not create a mutator method
for its slot: addImmutableSlot:valued: and addImmutableDelegate:valued:.
Next: 2.2 Expressions
Up: 2 Language Reference
Previous: 2 Language Reference
Contents
Index
Brian Rice
2004-10-30