Brian Rice and Lee Salzman
Slate is a member of the Smalltalk family of languages which supports an object model in a similar prototype-based style as Self[2], extended and re-shaped to support multiple-dispatch methods. However, unlike Self, Slate does not rely on a literal syntax that combines objects and blocks, using syntax more akin to traditional Smalltalk. Unlike a previous attempt at providing prototype-based languages with multiple dispatch, Slate is dynamic and more free-form. It is intended that both Smalltalk and Self styles of programs can be ported to Slate with minimal effort. Finally, Slate contains extensions including syntactic macros, optional keywords, optional type-declarations and subjective dispatch, that can be used to make existing programs and environment organizations more powerful.
Slate is currently implemented as an interpreter written in Common Lisp, which loads source files to build a full environment. A complete bootstrap is under development, which will involve many of the optimizations of the Self system.
Throughout this manual, various terms will be highlighted in different ways to indicate the type of their significance. If some concept is a certain programming utility in Slate with a definite implementation, it will be formatted in a typewriter-style. If a term is technical with a consistent definition in Slate, but cannot have a definite implementation, it will be set in SMALL CAPITAL LETTERS. Emphasis on its own is denoted by italics. When expression/result patterns are entered, typewriter-style text will be used with a Slate> prompt before the statement and its result will be set in italicized typewritten text below the line.
Finally, many of the examples assume that the full standard library
set has been loaded, or at least the fundamental set. To perform this,
execute:
'src/init.slate' fileIn.
when the interpreter is running. Additional libraries can be loaded
with a similar syntax.
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 block closure represents an encapsulable context of execution, containing local variables, input variables, the capability to execute expressions sequentially, and finally returns a value to its point of invocation.
Block closures 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,
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 value, as follows:
Slate> [3. 4.] value.
Nil
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:
Expressions in Slate consist of message-sends to argument objects. In Slate, the left-most argument is not considered the implicit receiver. This can mostly be ignored when invoking methods, however.
An important issue is that every identifier is case-sensitive in Slate, that is, there is a definite distinction between what AnObject, anobject, and ANOBJECT denote even in the same context. Furthermore, the current implementation is whitespace-sensitive as well, in the sense that whitespace must be used to separate identifiers in order for them to be considered separate. For example, ab+4 will be treated as one identifier, but ab + 4 is a message-send expression.
There are three basic types of messages, with different syntaxes and associativities: unary, binary, and keyword messages. Precedence can of course be overridden by enclosing expressions in parentheses. An implicit left-most argument can be used with all of them.
A concept that will be often used about message-sends is that of the name of a message, its SELECTOR. This is the symbol used to refer to the message or the name of a method that matches it. Slate uses three styles of selectors, each with a unique but simple syntax.
A UNARY MESSAGE does not specify any additional arguments. It is written as a name following a single argument.
Some examples of unary message-sends to explicit arguments include:
A BINARY MESSAGE is named by a special non-alphanumeric symbol and 'sits between' its two arguments. Binary messages are also evaluated from left to right; there is no special precedence difference between any two binary message-sends.
These examples illustrate the precedence and syntax:
A KEYWORD MESSAGE is an alternating sequence of keywords and expressions. Keywords are identifiers beginning with a letter and ending with a colon. Keyword messages start with the left-most argument along with the longest possible sequence of keyword-value pairs. The SELECTOR of the message is the joining-together of all the keywords into one symbol, which is the name of the message. For example,
Keywords have the lowest precedence of message-sends, so arguments may be the results of unary or binary sends without explicit grouping required. For example, the first expression here is equivalent to the latter implicitly:
Expressions occur between stop-marks, which are periods. At the top-level, expressions aren't evaluated until a full stop is entered. The stop mark also means that expression results aren't directly carried forward as an argument to the following expression; side-effects must be used to keep the results. More specifically, each expression in the sequence must be evaluated in order, and one expression's side-effects must effectively occur before the next expression begins executing and before any of its side-effects occur.
Slate provides for a bare expression sequence syntax that can be embedded within any grouping parentheses, as follows:
Slate> (7 factorial. 5
negated) min: 6.
-5
Within methods, blocks, and even at the top-level, some expressions may take the surrounding context as the first argument. There is a precedence for the determination of which object becomes the first argument, which is entirely based on lexical scoping. So, within a block, an implicit send will take the block's run-time context as argument, and then at lesser precedences will be the next outer contexts in sequence, up to the top-level and what it inherits from.
There are some very common uses of implicit-context sends. In particular, accessing and modifying local variables of a block or method is accomplished entirely this way, as well as returns. For example,
In some cases, it may be necessary to manipulate the context in particular ways. In that case, it can be directly addressed with a loopback slot named thisContext, which refers to the current activation. The essence of this concept is that within a block, x: 4. is equivalent to thisContext x: 4.1
METHODS in Slate are basically annotated block closures, 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 is found to contain the character, the rest of the same send is examined for other instances of the symbol. The parser then treats the expression or identifier to the right of the @ character as a dispatch target for that argument position. After the message-send, 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:
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:
[
c array at: index
].
[
c array at: index
].
on: {Set traits. NoRole}.
Message lookup in Slate involves all arguments in concert. Each object contains a table separate from its slot table that contain dispatch annotations per ROLE. An object's roles are its set of possible positions within a method that is defined upon it.
Object slots which are designated as delegate slots will be traversed recursively to continue the lookup process if the object does not define a method which matches the message's signature.
Slate takes a differing approach to dispatch algorithms as other programming languages involving multiple-argument dispatch. First, the algorithm is lazy, traversing as little as possible the delegation paths in order to find a first applicable method. This reduces the overhead of dispatch by eliminating extra steps due to a large number of implemented methods for the given protocol, or the possibility of a cyclical delegation relationship. Also, since traditional algorithms perform intersections on method-sets, there is an algorithmic complexity cost which is avoided.
Dispatch occurs as follows: delegations are traversed in lock-step, collecting matching methods found in each argument's corresponding role. A mapping of previously-seen methods to the relevant argument positions is retained. When a method has been seen in all argument positions, it is dispatched. Also worth noting is that for each argument position, a list of untraversed delegations is retained to handle multiple delegation.
Specifically, the set of argument objects themselves are first considered. For each, the methods in the specified role for the object that match the method's name are noted in the mapping of seen methods. If a method is seen to be mapped to all argument positions, it is then immediately dispatched. Once each argument has been examined in turn (and no dispatch has yet caused an early return), the first delegation for the argument replaces it for consideration, and the other delegations are placed into the worklist. If there are no delegations, the object at the front of the worklist is taken. Then the role-inspection process continues, with the same satisfaction condition. If the delegations are all exhausted with no method found, an error is raised, notifying that no method could be found.
There are two definite orderings that are important to consider. First, the arguments on the left are more significant to the dispatch than those to the right. So of two applicable methods, the one that matches the left-to-right order first is the one that is applied. Second, delegate slots that are searched in the reverse order that they were added: more recent additions override older delegates for the same object.
Finally, this procedure of matching messages with methods is convenient in that omitting a dispatch annotation because it is not necessary results in no change in the semantics of the method or whether or not it is applicable. Moreover, a sparser use of dispatch annotations means that fewer methods need to be collated during the lookup process, which generally means that the process will be faster.
So the recommended style of using dispatch annotations is minimalist, which helps for genericity and for one performance aspect. This also allows for a smooth transition from single-dispatch use without any conceptual or otherwise penalty.
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.
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.
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.
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.
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.
`evaluate provides compile-time evaluation of arbitrary expressions.
`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.
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:
Slate's default support for character literals uses the $ symbol as a prefix. The following printable and non-printable characters require backslash escapes as follows:
Character name | Literal
Escape |
All other symbols can be immediately be preceded by $ in order to construct the Character object for them, for example,
$a, $3, $>, and $$
are all Character object literals for a, 3, >, and $, respectively.
Strings are comprised of any sequence of characters surrounded by single-quote characters. Strings can include the commenting character (double-quotes) without an escape. Embedded single-quotes can be provided by using the backslash character to escape them (\'). Slate's character literal syntax also embeds into string literals, omitting the $ prefix. All characters that require escapes in character literal syntax also require escapes when used within string literals, with the exception of double-quote marks and the addition of single-quote marks.
The following are all illustrative examples of Strings in Slate:
Symbols start with the pound sign character (#) and consist of all following characters up to the next non-escaped whitespace, unless the pound sign is followed exactly by a string literal, in which case the string's contents become the identifier for the symbol. So, for example, #@, #key:word:expression:, #something_with_underscores, and #'A full string with a \nnewline in it.' are all valid symbols and symbol literals.
A property of Symbols and their literals is that any literal with the same value as another also refers to the same instance as any other symbol literal with that value in a Slate system. This allows fast hashes and comparisons by identity rather than value hashes. In particular, as with Slate identifiers, a Symbol's value is case-sensitive, so #a and #A are distinct.
Internally, Slate currently keeps one global table for symbols, and uses individual context objects to hold local bindings.3
Arrays can be literally and recursively specified by curly-brace notation using stops as separators. Array indices in Slate are 0-based. So:
Immediate array syntax is provided as an alternative to create the array when the method is compiled, instead of creating a new array on each method invocation. The syntax is identical except that the first opening brace is preceded by the pound sign. The disadvantage is that no run-time values will be usable.
The lobby is the root namespace object for the Slate object system. All 'global' objects are really only globally accessible because the lobby is delegated to by lexical contexts, directly or indirectly. The lobby in turn may delegate to other namespaces which contain different categorized objects of interest to the applications programmer, and this can be altered at run-time.
Every object reference which is not local to a block closure is sent to the enclosing namespace for resolution, which by default is the root namespace, the lobby (nested closures refer first to their surrounding closure). The lobby contains a loopback slot referring to itself by that name. To add or arrange globals, either implicit sends or explicit references to the lobby can be made. (Consider it good style to directly reference it.)
The lobby is essentially a threading context, and in the future bootstrap will be instantiable in that sense.
The lobby provides access to the major Namespaces, which are objects suitable for organizing things (for now, they are essentially just Oddball objects). The most important one is prototypes, which contains the major kinds of shared behavior used by the system. Objects there may be cloned and used directly, but they should not themselves be manipulated without some design effort, since these are global resources. prototypes is inherited by the lobby, so it is not necessary to use the namespace path to identify, for example, Collection or Boolean. However, without explicitly mentioning the path, adding slots will use the lobby or the local context by default.
The prototypes namespace further contains inherited namespaces for collections, and can be otherwise enhanced to divide up the system into manageable pieces.
Slate defines several subtle variations on the core behavior of objects:
There are various Oddballs in the system, and they are non-cloneable in general. However, Oddball itself may be cloned, for extension purposes.
Slate objects, from the root objects down, all respond to the message traits, which is conceptually shared behavior but is not as binding as a class is. It returns an object which is, by convention, the location to place shared behavior. Most Slate method definitions are defined upon some object's Traits object. This is significant because cloning an object with a traits delegation slot will result in a new object with the same object delegated-to, so all methods defined on that traits object apply to the new clone.
Traits objects also have their own traits object, which is Traits traits. This has the important methods defined on it for deriving new prototypes with new traits objects:
Slate's interpreter primitively provides the objects True and False, which are clones of Boolean, and delegate to Boolean traits. Logical methods are defined on these in a very minimalistic way.
Here are the logical methods and their meanings:
Description | Selector
AND/Conjunction |
Blocks that evaluate logical expressions can be used lazily in other logical expressions. For example,
In general, the basic of booleans to switch between code alternatives is to use ifTrue:, ifFalse:, and ifTrue:ifFalse: for the various combinations of binary branches. For example,
Conditional evaluation can also be driven by whether or not a slot has been initialized, or whether a method returns Nil. There are a few options for conditionalizing on Nil:
Slate includes various idioms for constructing basic loops.
All of the normal arithmetic operations (i.e. +, -, *, /) are supported primitively between elements of the same type. Type coercion has to be done entirely in code; no implicit coercions are performed by the virtual machine. However, the standard library includes methods which perform this coercion. The interpreter also transparently provides unlimited-size integers, although the bootstrapped system may not do so implicitly.
The following are the rest of the primitive operations, given with an indication of their "signatures":
There is an entire system for handling dimensioned units and their various combinations and mathematical operations. See the 'src/dimensioned.slate' file for an overview.
Slate's collection hierarchy makes use of multiple delegation to provide a collection system that can be reasoned about with greater certainty, and that can be extended more easily than other object-oriented languages' collection types.
Figure 1 shows the overview of the collection types, and how their delegation is patterned.
All collections support a minimal set of methods, including support for basic internal iteration and testing. The following are representative core methods, and are by no means the limit of collection features:
Collections delegating to ExtensibleCollection respond to add:, remove:, and other protocol messages based upon them, such as the batch operations addAll: and removeAll:.
Sequences are Mappings from a range of natural numbers to some objects, sometimes restricted to a given type. Slate sequences are all addressed from a base of 0.
To access and modify sequences, the basic methods seq at: index
and
seq at: index put: object are provided.
Arrays are fixed-length sequences and are supported primitively.
Subsequences allow one to treat a segment of a sequence as a separate sequence with its own addressing scheme; however, modifying the subsequence will cause the original to be modified.
Cords are a non-copying representation of a concatenation of Sequences. Normal concatenation of Sequences is performed with the ; method, and results in copying both of the arguments into a new Sequence of the appropriate type; the ;; method will construct a Cord instead. They efficiently implement accessing via at: and iteration via do:, and Cord as: Sequence will ``flatten'' the Cord into a Sequence.
An ExtensibleSequence is an extensible Sequence with some special methods to treat both ends as queues. Adding to an ExtensibleSequence will add at the last index, at the ``end''.
A SortedSequence behaves similarly except that it will arrange for its members to remain sorted according to a block closure that compares two individual elements.
A Stack is an ExtensibleSequence augmented with methods to honor the stack abstraction: push:, pop, top, etc.
A Range is a Sequence of Numbers between two values, that is ordered consecutively and has some stepping value.
A Buffer is a special ExtensibleSequence that takes extra care to only use one underlying array object, and also stores its elements in a ``wrap-around'' fashion, to make for an efficient queue for Streams (see BufferReadStream and BufferWriteStream (des:BufferReadStream)). One consequence of this is that a Buffer has a limited upper bound in size which the client must handle, although the capacity can be grown explicitly.
Strings in Slate are Arrays of Characters. Strings and characters have a special literal syntax, and methods specific to dealing with text.
NoDuplicatesCollection forms a special protocol that allows for extension in a well-mannered way. Instead of an add: protocol for extension, these collections provide include:, which ensures that at least one element of the collection is the target object, but doesn't do anything otherwise. Using include: will never add an object if it is already present. These collection types still respond to add: and its variants, but they will behave in terms of the include: semantics.
The default implementation of this protocol is Set, which stores its elements in a (somewhat sparse) hashed array.
Mappings provide a general protocol for associating the elements of a set of keys each to a value object. A Dictionary is essentially a Set of these Associations, but they are generally used with symbols as keys.
Mapping defines the general protocol at: and at:put: that Sequences use, which also happen to be Mappings. Mappings also support iteration protocols such as keysDo:, valuesDo:, and keysAndValuesDo:.
A LinkedCollection provides a type of collection where the elements themselves are central to defining what is in the collection and what is not.
The usual LinkedList type, comprised of individual Links with forward and backward directional access, is provided as a flexible but basic data structure.
Slate includes libraries for binary trees, red-black trees, trees with ordered elements, and tries.
A directed graph, or Digraph (directed graph) type, is provided with associated Node and Edge types. A KeyedDigraph provides the same behavior with a keyed access, similar to that in a Mapping, although there is an allowance for various kinds of non-determinism, which makes this useful for creating Non-deterministic Finite Automata.
Slate includes the beginnings of a mathematical vector and matrix library.
Streams are objects that act as a sequential channel of elements from (or even to) some source.
Streams respond to a number of common messages. However, many of these only work on some of the stream types, usually according to good sense:
Figure shows the major stream types
and their relationships.
The Slate interpreter provides two Streams primitively, ConsoleInput and ConsoleOutput, which are Read- and WriteStreams by default.
Each collection type may define its own Stream type which goes over its elements in series, even if the collection is not ordered, and only visits each element once. This type's prototype is accessed via the slot Iterator within each collection. So Set Iterator refers to the prototype suitable for iterating over Sets.
In order to create a new iterator for a specific collection, the iterator message is provided, which clones the prototype for that collection's type and targets it to the receiver of the message.
File access in Slate is currently rudimentary. The interpreter provides an object type FileStream which follows the corresponding protocol:
In coordination with the reserved syntax for type-annotation in block headers, Slate's standard libraries include a collection of representations of primitive TYPES as well as quantifications over those types.
The library of types is laid out within the non-delegated namespace Types in the lobby.
Local slot specifiers in a Method header as well as input slot specifiers may have types optionally declared within the header. Within a method declaration expression, the input slots may be redundantly specified in the header as well as in the dispatch expression. However, if this is done, the header's specifier needs to be specified as an input slot and if multiple input slot types are specified, they should be specified in order.
The syntax is similar to that for @-based dispatch notation: follow the slot name with the bang character ``!'' and then a type expression, which may be a primitive or derived type. For example,
A simple module system is provided, designed to capture the bare essentials of a collection of coherent code. The current module system is just associated with each library file for simplicity's sake. The methods provides: and requires: applied to the context will add to and check against a global features sequence respectively, and missing requirements are noted as the code is loaded. Again for simplicity, features currently contains and expects Symbols. The primitive fileIn method also invokes a hook to set the currentModule in its context.
Slate provides an unusual opportunity to organize programs and environments in unique ways, primarily through the unique object-centered combination of prototypes and multiple-argument dispatch.
New namespaces should be used for separate categories of concepts. Occasionally, these are the kind that should automatically included in their enclosing namespace (which can be further inherited up to the lobby). This is done simply by placing the new namespace object in a delegate slot.
These represent objects such as specific colors with well-known names, or cloneable objects with useful default values. Generally these should have capitalized names if they are cloneable, and can be capitalized or lowercase if not. For cases with a large group of such value objects, like colors, there usually should involve a separate namespace to avoid cluttering up the surrounding one. This also helps with naming the use of a value if the intuitive interpretation of its name is dependent on context.
One of the primary benefits and peculiarities of Smalltalk's style of method syntax is that it provides an opportunity to name one's protocols using something resembling a phrase. Usually, it is recommended to re-use protocols whenever describing similar behaviors, as an aid to the user's memory in matching functionality to a name to call. Here are some general practices that Slate uses which have been common in Smalltalk practice for years.
Attributes are perhaps the simplest to name of all, in that they are generally nouns or noun phrases of some sort, whether used as direct slots or methods which calculate a property dynamically.
Methods which test for some fact or property about a single object are generally given a ``whether''-style phrase. For example, myCar isRed answers whether one's car is red. Slate offers an additional idiom over this particular style, in that myCar color is: Red is also possible, since is: looks at both the subject and the object of the query.
While the method clone is the core of building new objects in Slate, rather than instantiating a class, there is still the need to provide an idiom for delivering optional attributes to one's new objects. Generally, these methods should start with new- as a prefix to help the reader and code user to know that the original object will not be modified, and that the result is a new, separate individual. These methods are usually keyword methods, with each of the keywords describing each option, whether literally naming an attribute, or simulating a grammatical phrase using prepositions.
The most interesting protocols are akin to commands, where one addresses the objects in question with a phrase that suggests performing some action. This should usually have one key verb for each major component of the action (there is usually just one action per method, but select:thenCollect:, for example, performs two), and prepositions or conjunctions to relate the verbs and nouns.
These are perhaps the most controversial of any programming language's protocols. In the Smalltalk family of syntax, there are no precedence orderings between operators of different names, so the issues with those do not arise. However, it is very tempting for the library author to re-use mathematical symbols for her own domain, to allow her users to have a convenient abbreviation for common operations. While this benefits the writer of code which uses her library, there are domains and situations that punish the reader of the code that results.
For example, mathematical addition and multiplication symbols, ``+''
and ``*'', are generally associative and commutative.
That is, repeated calls to these should be able to re-order their
arguments arbitrarily and acheive the same result. For example,
.
However, string concatenation (as an example) is not commutative;
we cannot re-order the arguments and expect the same result, i.e.
"gold"+"fish"="goldfish"
, whereas "fish"+"gold"="fishgold"
. Because concatenation is associative, however, we can re-use
the punctuation style of the semi-colon ``;'' and achieve
intuitive results. This general style of reasoning should be applied
wherever this type of operator name re-use could arise.
Often there are situations whether the user will want to specialize a method in some argument position for a specific object. There are various reasons to do this, and various factors to consider when deciding to do so.
Two common patterns where the developer wants to specialize to a single object emerge from using Slate. First, there are domain objects which naturally have special non-sharable behavior. For example, True is clearly a particular object that helps define the semantics of the whole system, by representing mechanical truth abstractly. In other situations, the same pattern occurs where one has a universal concept, or locally an absolute concept within a domain.
Second, there are situations whether the user is demonstratively modifying the behavior of some thing in order to acheive some prototype that behaves in some situation as they desire. Depending on whether the user decides to share this behavior or not, the instance-specific behavior may or may not migrate to some shared Traits object. In either case, this is an encouraged use of objects and methods within Slate.
There are factors which weigh against the use of dispatch on objects with non-shared behaviors. Generally, these just amount to a few simple reasons. First, the behavior will not be shared, which is obvious, but sometimes not clear to the author. Second, the author may mistake an object for its value or attributes, such as Strings, which are not unique per their value, and so get unexpected results if they dispatch on a String instance. The same is true for most literals of that nature, with the exception of Symbols.
The nature (and current limitations) of defining objects, relations, and the methods that operate over them require a certain ordering at this point which is worth mentioning. The central point of constraints is the definition of dispatching methods: these methods must have their dispatch targets available at the time they are evaluated. Since there is no late-binding yet of dispatch expressions, generally the basic construction of one's traits and prototype definitions must all occur before defining methods which dispatch to them. The definition needs merely to introduce the actual object that will be used later; other features of the objects, such as what other methods are defined upon it, are late-bound and will not hinder a method-dispatch expression.
In general, however, it is recommended to define methods in a bottom-up fashion: that more basic utilities should be introduced before the methods that use them. This allows the user (and the author) of the code to read the program sequentially as a document and have some understanding of a program's components when only the name of the component is seen. Of course, this is not always possible, but it helps often enough.
This document was generated using the LaTeX2HTML translator Version 2K.1beta (1.48)
Copyright © 1993, 1994, 1995, 1996,
Nikos Drakos,
Computer Based Learning Unit, University of Leeds.
Copyright © 1997, 1998, 1999,
Ross Moore,
Mathematics Department, Macquarie University, Sydney.
The command line arguments were:
latex2html -no_subdir -split 0 -show_section_numbers progman.tex
The translation was initiated by Brian Rice on 2003-07-21