Smalltalk
Since around June 2008, Étoilé has included a Smalltalk Just-In-Time (JIT) compiler.
Smalltalk is a simple, pure object oriented, language originating at Xerox PARC in the '70s. Objective-C, in which GNUstep and the core of Étoilé are written, is an attempt to combine Smalltalk and C into a single language. Everything you see between square brackets in Objective-C can be thought of as'inline Smalltalk' in a C program. For example, the following are Objective-C and Smalltalk versions of the same thing:
Objective-C:
[object messageWith:anArgument];
Smalltalk:
object messageWith: anArgument.
As you can see, the two are very similar. The main difference between Smalltalk and Objective-C is that this is all that Smalltalk does - everything is done in terms of sending messages to objects - while Objective-C uses C syntax for a lot of things.
One particular example of this is flow control. If you want to execute something based on whether a condition is true, then you send it an ifTrue:
message with a code block as the argument, like this:
condition ifTrue:[ 'The condition was true.' log. ].
A block is a Smalltalk construct which creates an object representing a block of code. It responds to a value
message, which causes it to execute and return the value of the last expression to execute. This simple snippet will run the block if condition
decides that it in some way represents a true value.
Blocks can take arguments too. A common use of blocks is performing permutations on collections. For example:
array map:[ :x | x transformWith:y. ].
This block will respond to a value:
method and will run the code with the value of x
set to whatever was passed in as a parameter. The array will send this block a value:
message with each element in the array as an argument. You will get back an array with the result of sending a transformWith:y
message to every object in the array. Note that y
is not declared inside the block. Code inside blocks can reference any variables that are valid when the block is created.
The dialect of Smalltalk used by Étoilé is similar to that provided by GNU Smalltalk. The main difference you will encounter is that the class libraries are different. While most Smalltalk implementations come with the set of classes found in Smalltalk-80 and some extras, the Étoilé implementation is designed to work closely with GNUstep. The core objects provided by the system (e.g. collection classes) are all from OpenStep. You can send messages to Objective-C classes and objects as if they are Smalltalk objects (in fact, Smalltalk objects and Objective-C objects are represented in exactly the same way at run time). For example, you can create an array and add objects to it like this:
a := NSMutableArray array.
a addObject: b.
a addObject: c.
You can also use the shorthand form:
a := {b. c}.
These two are functionally equivalent, although the second form is slightly more efficient.
Smalltalk uses parentheses to indicate precedence. So, if you wanted to log the second item in the array created above, you would use:
(a objectAtIndex:1) log.
To create classes in Smalltalk, you send a subclass:
message to an Objective-C or Smalltalk object, for example:
NSObject subclass: LS
[
run
[
| fm files out |
fm := NSFileManager defaultManager.
files := fm directoryContentsAtPath:'.'.
out := NSFileHandle fileHandleWithStandardOutput.
files foreach:[ :x | x printOn:out. '\n' printOn:out. ].
]
]
This creates a subclass of NSObject
(the OpenStep base class) with one method, run
. If this code is fed to the Smalltalk launcher tool, then an instance of this class will be created and sent a run
message.
Inside the run
method, three local variables are declared. fm
is set to the default NSFileManager
instance (this class is a singleton, so you only get one per program). The local variable files
is then set to an array of all of the files in the local directory. This array is then sent a foreach:
message. This message causes it to run the block passed as an argument with every object in the array, in order. It will print each of the items in the array, followed by a newline. This is therefore a very simple implementation of the standard UNIX ls
tool.
Categories (sets of methods added to an existing class) can be written in Smalltalk by sending the class an extend
message, instead of a subclass:
message.
Foundation Data Types
A lot of things in OpenStep are not quite purely object oriented. OpenStep was originally designed to run on 25MHz Motorola 68K machines, and in some places a pure object system was sacrificed for speed. There are a small number of C structures in the Foundation and AppKit libraries that are used in a lot of places. These include NSRect
, NSRange
and a few others. In Objective-C, you would create a substring like this:
NSRange r = NSMakeRange(0, 9);
NSString substr = [@"Substring of longer string" substringWithRange:r];
Here, r
is a C structure with two elements, a location and a length. This is then passed as an argument to the substringWithRange:
message. Smalltalk, however, is a pure object-orented language. It does not have structures. This was a problem for OpenStep in the past. It is sometimes convenient to put these structures into an OpenStep collection class, like an NSArray
or an NSDictionary
. Unfortunately, these only support objects. The solution to this is the NSValue
class, a class used to wrap ('box') a primitive C type.
You can create an NSValue
representing a range in Smalltalk like this:
range := NSValue rangeWithLocation:0 length:9.
In Objective-C, you would then send this a rangeValue
message to get the primitive C type. In Smalltalk, you don't need to do this, you just use it directly:
substr := 'Substring of longer string' substringWithRange:range.
Behind the scenes, the Smalltalk compiler introspects the substringWithRange:
method and learns that it expects an NSRange
as a parameter. It then sends the range
object a rangeValue
message and passes the result as the argument, instead of the range
object itself. This means you can use any Objective-C object (including one of your own creation) as long as it responds to a rangeValue
message.
You can even use Smalltalk objects, as long as they implement a rangeValue
method. Since the compiler knows that rangeValue
must return an NSRange
, anything the Smalltalk object returns from its rangeValue
will also have rangeValue
called on it - at some point you need some Objective-C glue to create the primitive type, but it can be via as many layers of indirection as you please.
This same boxing mechanism is used to turn Smalltalk SmallInt objects into C integers and vice-versa. Any object responding to an intValue
message can be passed to a method expecting a C int
, or returned from a method returning a C int
.
Note that it is very important that every selector only has a single type. If you declare two methods called count
in Objective-C, one returning an int
and one returning an object, this will cause problems. This is generally bad style in Objective-C, since if you are passed an id
and send it a count
message you then don't know whether the thing you get back is a primitive integer or an object. In Smalltalk, it is even more of a problem, since the compiler doesn't know, and so may well pick the wrong result. Fortunately, you can not introduce this problem by writing Smalltalk, only by writing Objective-C.