|
|
Articles in the Extending Smalltalk series:
The Squeak Curly Brace Extension to Smalltalk
written by Peter William Lount version 1, 20040920 12:36pm PDT revision 2, 20040921 8:50am PDT, Thanks MFT for the corrections, Added "nesting sidebar". In general I support extensions to the Smalltalk Language that make sense, that keep the syntax clean, that have - and this is important - multiple overlapping purposes and benefits that provide new capabilities that wouldn't be possible otherwise. If it's just to have a second way to do things then it's generally not needed unless there is some other compelling benefit. For example, Squeak extends Smalltalk to create a short cut for filling Arrays with object instances. It uses "curly braces" surrounding standard Smalltalk expression statements and by doing so it reduces the amount of "verbage" significantly. It's nice in some cases. (Note that this format is different that the Smalltalk "literal array" format in that the expressions are evaluated at "run-time" for the curly brace form rather than at "compile-time" for the "litteral array" form. The difference is significant with the literal array format being "static" once it's been compiled and the Squeak curly brace approach being fully dynamic (or static) at run-time.) Here's the standard Smalltalk way to fill an Array or other Collection instance.
aList := OrderedCollection new. aList add: 100. aList add: 250 * 10. aList add: 'peter'. aList add: (Point x: 10 y: 20). If you were filling an Array you'd either send "asArray" to the "aList" above or you could do:
aList := Array new: 4. aList at: 1 put: 100. aList at: 2 put: 250 * 10. aList at: 3 put: 'peter'. aList at: 4 put: (Point x: 10 y: 20). The result of the above two examples is either an instance of an OrderedCollection or an Array with four elements. Here's the standard Smalltalk shortcut to reduce verbage while filling an Array or other Collection instance. It uses "cascading message sends" which send a sequence of messages to the same receiver. The cascading messages sends are seperated by the semicolon, ";", character.
aList := OrderedCollection new. aList add: 100; add: 250 * 10; add: 'peter'; add: (Point x: 10 y: 20). This can be shrunk even further like so:.
aList := (OrderedCollection new) add: 100; add: 250 * 10; add: 'peter'; add: (Point x: 10 y: 20); yourself. The message "yourself" asks the receiver of the message to return itself. In the above context of a cascading message sequence of messages the object that is the result of the very last message is what gets assigned into the variable "aList". By having the last message be "yourself" the new instance of the Array will be the object placed into the variable, and not the result of the last "add:" ( by convention the "add:" collection message protocol returns it's parameter not the receiver of the message). Another variation without the usage of "yourself" that moves the assignment of the "aList" into the "round" parentheses, "( )".
(aList := OrderedCollection new) add: 100; add: 250 * 10; add: 'peter'; add: (Point x: 10 y: 20). Now that's about the shortest that this can be shrunk unless you remove the white space. With the Squeak Smalltalk Curly Braces Array creation extension the following is possbile.
aList := { 100. 250 * 10. 'peter'. Point x: 10 y: 20 }. And it even begins to be quite readable as a one liner.
aList := {100. 250 * 10. 'peter'. Point x: 10 y: 20 }.
Nice. Now that's significantly shorter and much easier to read. You'll notice that this syntax is the same as a sequence of regular Smalltalk statements but with the curly braces added around them to create the Array. Here is the sequence as regular Smalltalk statements. Notice that unless we remember the resulting object from each statement they'll be forgotten.
100. 250 * 10. 'peter'. Point x: 10 y: 20. The difference is the curly braces adds the creation of the array to collect the results of the evaluation of each expression. Normally the results of Smalltalk expressions are either used as a message parameter or stored into a variable or thrown away. This special curly brace syntax "collects" the objects and stores them into a new Array instance of the required size. If you don't want an Array instance then you can convert it into what ever collection you wish like so:
aList := {100. 250 * 10. 'peter'. Point x: 10 y: 20 } asSet.
aList := {100. 250 * 10. 'peter'. Point x: 10 y: 20 } asOrderedCollection.
Oh, since these are full Smalltalk expressions you can do pretty much whatever you like including variable assignments within the curly braces! (Acually you can do this with the message cascading as well).
aList := {100. 250 * 10. 'peter'. aSpot := Point x: 10 y: 20 }.
The variable "aSpot" will contain a reference to the instance of the Point object that is also in the collection. Very nice. This "curly brace object collection" short cut simplifies methods that are used in creating "networks" (graphs) of object instances and "hooking" them up to each other. This short cut enables shorter and much easier to read Smalltalk code. This is an extension that I vote for Smalltalk's to incorporate. In fact the Zoku Langauge variant of Smalltalk, that I'm creating, has a variation of this extension as one of the useful extensions and modifications of Smalltalk syntax. It's interesting to note that I wasn't aware of the Squeak version until after defining the Zoku Language. It's true that form follows function. What I wanted for Zoku was the ability to collect the objects results from executing a series of statements. In Zoku you'd ask a Block to evaluate while collecting the objects from each statement by sending it the message "objects" to the Block instance like so:
aList := [100. 250 * 10. 'peter'. aSpot := Point x: 10 y: 20 ] objects.
In the above case the block returns a collection consisting of all the objects. In normal use a block receives the message "value" and returns the object from the last expression statement evaluated. In the example below the last object is the Point instance.
aList := [100. 250 * 10. 'peter'. aSpot := Point x: 10 y: 20 ] value.
By using a message rather than syntax the Zoku block is flexible since it doesn't add any new syntax (i.e. like Squeak does by adding the curly braces) just a new message to Blocks that perform a evaulation of a block within the virtual machine slightly differently. The advantage of the Squeak appraoch is that there is no need to send a message to the Block. The disadvantage of the Squeak approach is that it requires a new syntax.
Nesting in Smalltalk
For those of you new to Smalltalk the code within the "square brackets", "[ ]", is known as a Block, "Block Context", "block closure" or simply
a "closure". It's a chunk of the program code that is "deffered" and "objectified" as a "BlockContext" object that is a first
class object like any other in the Smalltalk system. Blocks evaluate when they receive the message "value", "value:", "value:value:" or
other variants. Blocks can take parameters and in some versions of Smalltalk they can even define their own "temporary variables" within
the block context. Think of blocks as code that you can pass around and control when it's executed. Similar to "functions" in C or LISP
but much more powerful since they are also first class objects.( ) The round paraenthesis are used for the nesting of complex expressions within other expressions. Also used to claify code and to ensure the "order of precedence". Evaluation is immediate. This is equalivant to the use of the "curly braces", "{ }", in other popular programming languages like Java, C, C++, etc... [ ] The square brackets are used for the nesting and definition of "deferred" expressions known as "blocks", "block context", "block closure", or "closures". Blocks are "first class objects", may have "parameters", (may have temporary variables in some Smalltalk's, and are only evaluated when they receive the "value", "value:", "value:value:" or variant message. Used extensively within Smalltalk for control structures, looping, dynamic code and other purposes. Most popular computer lanuages have nothing like blocks. { } Squeak Smalltalk uses the curly braces to "collect" the results of the full statement expressions, which are seperated by "periods", into a new Array instance. Evaluation is immediate. The statements are fully evaluated at "run-time" so they can be fully dynamic. Much more flexible than "literal array" definitions. #( ) The "literal array" uses the "pound plus round parenthesis" to define "literal arrays", that is arrays of simple "literal objects" such as numbers, strings, string symbols, etc... They are "static" since they are evaluated at "compile-time". The future is wide open with Smalltalk speading to many new markets such as Java, MacOSX, and embedded versions. While not all Smalltalk's are the same the basics are there. Let's collect and document all the extentions and differences of the Smalltalk's out there to find a common ground for future extensions to the Standard Smalltalk; or, at least, so that they are well known.
Articles in the Extending Smalltalk series:
Copyright 1 9 9 9 - 2 0 1 0 b y S m a l l t a l k . o r g "! , A l l R i g h t s R e s e r v e d . |
|