“Clean Slate” Smalltalk
- Brian Rice,
- Lee Salzman
- for: Smalltalk Solutions 2003
- Slate is a new free (MIT license) dialect and environment of Smalltalk:
- The primary motivation is to use as many good ideas from the Smalltalk community within a system built and designed from scratch.
- The number of new ideas in Slate is quite small. Bringing together all the ideas we found compatible is the new thing, and intentionally designing a system to support all of them is also new.
Why a new Smalltalk?
- Smalltalk itself hasn't changed much in 20 years.
- Self and Strongtalk promised some change. We have Java instead, and slowly some change in the more traditional Smalltalk-80 implementations.
- The commercial Smalltalks have customer bases to honor, existing investment, etc.
- Squeak is self-hosted and malleable, but has too many small issues: new designs can't tackle all of them.
- The right combination of ideas.
- Self, Strongtalk – object model, optimization practices.
- Common Lisp / CLOS – syntactic abstraction, meta-level objects, run-time organization.
- Dylan – optional static typing, multi-methods (and libraries that use them), and Lisp ideas in a less pure syntax.
- Slate development started in May, 2002:
- We were initially inspired by the Self concepts (for some time), but felt that the language and system had strayed too far from practical concerns.
- A year ago, the Squeak community had been approaching issues with modules and organizational change.
- Work on a Squeak to binary compiler was found to benefit strongly from a more powerful semantics in the language.
Slate the language
- Slate's semantics are built on a combination of prototype-based objects and closure-based methods.
- Methods are blocks that close over their arguments, and have a syntax signature that activates them.
- Particularly, Slate allows for multiple, dynamic inheritance, and multi-argument dispatch.
- We emphasize the combination as “PMD”.
- This combination has a number of implications:
- Methods are not given privileged or implicit access to their first/"receiver" object. Really, all objects are the receivers, and they play different roles.
- Objects do not relate to methods in the same way that they do to their slots. Methods are cooperatively held between many objects, and each object must know what roles it can take in each method.
- self/super keywords are inappropriate conceptually; instead all arguments are referred to by names from the method signature, and changing the order of lookup is accomplished through messages to the context: resend, sendTo:, and sendTo:through:.
- Method lookup is much less trivial: it's performed in cooperation between the arguments. Objects' dictionaries are searched in tandem, and first-found method sets are intersected to determine the first useful method.
- The semantics allow us to express systems more powerfully, but also forced us to consider optimization issues earlier in the design cycle.
- Slate has started with Self's inheritance model, using special slots to store delegates.
- This has been found to be too dynamic a pattern much of the time, and mixes the distinction between the direct object and its description too freely.
- This results in some bugs in corner cases of Slate's current traits system.
- A future release will present a cleaner, more appropriate interface.
- Our first rule was to keep as much traditional syntax as made sense.
- Method definitions became signatures with dispatch notations, followed by a block body.
- The “@” operator was used for dispatch, but we have freed up “!” by using “.” for top-level statement separators.
- Cascaded sends have been omitted for now, using “;” for concatenation instead – it's a style consideration for moving away from “receiver” - “arguments” distinction.
- The header must be delimited by vertical bars, so
- [:input | local | becomes...
- [| :input local |
- This removes ambiguity over local names appearing to be messages to the context.
- Periods separate expressions, and empty expressions between or after separators return Nil.
- There's also no implicit “^ self”! The programmer has more explicit control, but also the code is more manifest about return values.
- Let's look at some basic methods...
- Examples of double-dispatch elimination...
- Examples where tail-call elimination is used...
How “PMD” benefits the libraries
- Simple utility methods such as “is:” and “as:” are trivial to work with and extend.
- Prototype-centered “instantiation” allows one's plans or templates to adapt dynamically: this gives us parametrized collections nearly for free.
- Singletons become much simpler to work with. No extra wrappers in the form of singleton classes or double-dispatch selectors are needed.
- We have attempted to extend the Strongtalk use of multiple inheritance to separate collections into components which are more akin to types.
- (See diagram of the heterarchy...)
- Slate is “VM-less”: the image contains the binary code that implements the primitives. A stub loader is used to launch the image.
- Well, it will. It's currently hosted within a Lisp image which works in much the same way.
- However, we still have and maintain a bytecode specification and implementation which can be used to generate a true standalone VM.
- The real center of implementation is the compiler...
- We're developing a Slate-to-binary compiler based on Squeampiler ideas, but going beyond them.
- We rely on an intermediate representation which is not source-equivalent, but instead represents a control-flow graph expression.
- Basic blocks and branches are directly expressed with continuation support, and SSA form is used for data-flow expression.
- This is extensible by writing new low-level routines in Slate's “primitive mode”, which is essentially our replacement for Squeak's Slang-to-C translation.
- Slate's initial bootstrap will consist of writing out an image of binary code for methods and objects.
- The code will be instrumented to support interactive inspection and (re-)compilation.
- The following process will consist of importing the core libraries up to the point of development self-sufficiency.
- The current design of the exception system borrows from Dylan's “conditions”, which are classes with exception-style capabilities.
- Condition handlers, however, also are fully-fledged objects, and this allows for some extended programmatic use of conditions.
- By default, Slate does not provide native error-handling, so Lisp conditions are thinly wrapped for now.
- Slate has no (advertised) support for concurrency yet: we intend to build this around both the underlying compiler continuation model and some relatively safe protocol for native threads.
- We are also looking at the Squeak-E project's use of eventual-sends and other idioms for minimizing deadlock conditions while not involving the user in details about semaphores and monitors.
- The main set of libraries already consist of roughly 20,000 lines of code.
- Further libraries for the graphical interface and the bootstrap and low-level libraries basically double that, but will not be releasable for another month or two.
- Interface architecture... presentations and CLIM ideas, MVC – Morphic continuum, medium abstraction.
- MOP: safe inheritance composition system, generative programming framework, and an elaborate meta-object protocol that can benefit from an open extensible compiler / run-time for performance.
- Smalltalk compatibility mode: the old Smalltalk-in-Self with a browser, and a translation layer.
- Many useful libraries! Networking, XML, ...
The Slate Community
- The focus is to distill what's out there.
- What would you like to see?