[prev] [up] [next]

Smalltalk for Java/JavaScript guys

Introduction

Java and JavaScript inherited a lot of ideas and mechanisms from Smalltalk. Especially JavaScript has a lot in common with Smalltalk (actually, semantically, it is much nearer to Smalltalk, than to Java). Don't be fooled by syntax - it's the semantic which counts. This document shows some of them.

Please try to understand them.
It may also be very interesting to read, if you are a smalltalker !

Syntax - So you can read Smalltalk Code

First some info on syntax. It is often stated, that the Smalltalk syntax is strange, weird and hard to read. Actually the opposite is true - it is you being used and trained to the C-like syntax. First of all, Smalltalk is older, so never blame Smalltalk for not adapting the syntax of the reset of the world...

Variables

Smalltalk variables are actually names for a binding of a name to a value. The binding consists of an association of a name (a so called symbol) to an object (the so called value). Technically, this is implemented by a pointer: you can also think of every name being "a pointer to some object".

The variable is untyped - it can hold a reference to any object. However, the object itself "knows" what it is. It knows the class of which it is an instance.

This is very similar to how JavaScript deals with variables.

Local Variables

Within a method (which corresponds to a "virtual function") or a Smalltalk block (which corresponds to an inner, anonymous function), local variables are declared by placing them inside vertical bars. Thus, the JavaScript code:
    var foo, bar, baz;
    var a, b, c;
is equivalent to:
    |foo bar baz a b c|
Variable declarations must be placed at the very beginning of a scope (i.e. method or block). In contrast to JavaScript, where variable declarations can also occur later in a function (however, in JavaScript, the scope is also "the whole scope", so some coding guidelines prefer to also force programmers to put those declarations at the beginning, to not confuse readers of the code).

Statements

In Smalltalk, statements are separated by periods (much like english sentences are). In Java and JavaScript, they are delimited by semicolon. Notice the difference: "separating" vs. "delimiting". In Smalltalk, the last statement within a method or block does not need a period (because there is nothing to separate it from), whereas in Java/JavaScript, every statement needs its semicolon, except for the brace-block construct, which does not. (in Smalltalk, really EVERY statement needs its period, unless it is the last one). It does not hurt, to add a period after the last statement in Smalltalk (just think of the rest as being an "empty statement").

So, the javaScript:

    <statement1> ;
    <statement2> ;
    <statement3> ;
looks in Smalltalk like:
    <statement1> .
    <statement2> .
    <statement3> .
with the last period being optional.

Assignments

All three languages are not functional languages (although a functional programming style is definitely possible and often a good choice, the major paradigma is an "imperative style operating on objects". So, all of them offer the assignment. In Smalltalk, to avoid confusion with the equality comparison operators, it is written as ":=", whereas in Java/JavaScript, you would simply write "=". So:
    var a, b, c;

    ...
    a = 5;
    b = 4;
    c = a;
    ...
becomes in Smalltalk:
    |a b c|
    ...
    a := 5.
    b := 4.
    c := a.
    ...

Message Sends (virtual function calls)

The term "message send" is called "virtual function call" in many other languages. This may have historic reasons, one being probably, that Java inherits a lot of nomenclature from the C/C++ world, for obvious reasons.

Every such message send invokes a function (called method in Smalltalk) of the "receiving" object; it is the receiver's class, which determines which method will actually be executed. That is almost the same behavior for all three languages.

The syntax is slightly different:

Unary Messages (no argument passed)
For message sends (calls) without argument, simply write the name of the message (the function name), after the receiver:
    receiver.foo();
becomes in Smalltalk:
    receiver foo.
(without parenthesis). As a more concrete example, consider:
    var a, b, c;

    ...
    a = b.abs();
    c = a.foo();
    ...
which becomes in Smalltalk:
    |a b c|
    ...
    a := b abs.
    c := a foo.
    ...
Such messages (function calls) are called "unary messages", and the corresponding methods, which implement the response to such a call are called "unary methods". "unary" means, that there is no argument passed with the call.
Keyword Messages (arguments passed)
If arguments are to be passed, Smalltalk uses a syntax which combines argument count, names and position information in the name of the method (function): instead of placing the call-arguments into a parenthized list after the name of the called function, the Smalltalk code slices those right into the name, each separated by a colon. Because these fragments look like calls with named parameters, they are called keyword messages.

The name of that function is actually the concatenation of multiple parts, including the colon(s), to distinguish it from the above unary messages. In Smalltalk, every function which expects an argument must have colons in its name (one for each argument). Thus, the number and position of the arguments is fixed and defined by the function's name. So, a call to a "fooBar" function, with 2 arguments,

    var a, b, c;

    ...
    a = b.fooBar(1, 2);
    c = a.fooBarBaz(1, 2, a);
    ...
becomes in Smalltalk:
    |a b c|
    ...
    a := b foo:1 bar:2.
    c := a foo:1 bar:2 baz:a.
    ...
there is no direct 1-to-1 mapping of names possible: the colon is not a valid character in a name in java/JavaScript.

Notice, that it is possible in JavaScript, to call a function with a wrong number of arguments:

    var a, b, c;

    ...
    a = b.fooBar(1);
    c = b.fooBar(1, 2, 3);
    ...
in Java, this is cought by the compiler, which will bark at you. In Smalltalk, you cannot use the same name for 1 or 3 arguments; one of the names must be "fooBar:",the other something like "foo:bar:anyThingElse:" because there are three arguments.

Sometimes, especially when code is generated automatically from Java/JavaScript like languages, methods use a single underscore character to separate arguments, as in:

    x.someGeneratedName:arg1 _:arg2 _:arg3.
But still: do not forget that "foo:" and "foo:_:" and "foo:_:_:" are three different names for three different methods.
Also notice that "foo:bar:" is a different name from "bar:foo:".
Binary Messages
Although Smalltalk is relatively minimalistic in its syntax, and the above keyword messages would (semantically) suffice for all needs, the designers of the Smalltalk language thought that arithmetic messages look quite hard to read, if written like:
    (x add:1) mul:5.
Therefore, a special syntax for binary messages like +, -, * etc. is provided, which allows for those special characters to be written like infix operators. However, as these are still messages (virtual function calls), the language does not imply any meaning into those. Therefore, no precedence or grouping as in Java/Javascript is associated to those. They are simply evaluated from left to right. That may make mathematical expressions hard to read, as you have to use parenthesis for grouping. So, the Java expression:
    a + b * 5 + 4
MUST be written as:
    a + (b * 5) + 4
in Smalltalk. Otherwise, the expression would simply be evaluated left to right, giving a wrong answer.

This is a bit annoying for beginners, experienced Smalltalkers use parenthesis and don't even think about it. (Actually, making the evaluation order explicit using parenthesis is considered good style anyway: you dont have to think if the shift operator has precedence over a comparison or not.)

Anonymous (inner) Functions
One of the biggest differences lies in the use of anonymous functions (called block in Smalltalk). These play a minor role in Java and are occasionally used in JavaScript (as callbacks), but are a major program building element in Smalltalk. A Smalltalk block consists of a number of statements (separated by period), written in brackets, as in:
    myBlock := [ a foo. b bar ].
which corresponds to the JavaScript code:
    myFunc = function () { a.foo(); b.bar(); };
as you can see, Smalltalk is a bit shorter and less confusing in its syntax. By the way, there is nothing comparable to this in Java, because blocks are not only wrapping the contained statements, but also allow full access to the visible variables, can be assigned to variables, be stored in other objects or be returned from a method call or block evaluation:
    |m1 m2 outerBlock|

    m1 := 1233.
    outerBlock :=
	[
	    |o1 o2 innerBlock|

	    o1 := 1.
	    o2 := m1 + 1.
	    innerBlock :=
		[
		    |i1 i2|

		    i1 := o2 + m1 + 4.
		    [ i1 + 1 ]
		].
	    innerBlock
	].
corresponds to:
    var m1, m2, outerFunc;

    m1 = 1233;
    outerFunc = function() {
	    var o1, o2, innerFunc;

	    o1 = 1;
	    o2 = m1 + 1;
	    innerFunc = function() {
		    var i1, i2;

		    i1 = o2 + m1 + 4;
		    return function() { i1 + 1; };
		};
	    return innerFunc;
    };
If a block gets evaluated, its inner statemens are evaluated, and the block's return value is the last inner statement's expression-value. So, inside a block, there is no return-statement in Smalltalk, only expressions which are evaluated one after the other for the last one to provide the return value of the block.

Blocks can have arguments, these are listed at the beginning, each with a trailing colon, before a vertical bar, as in:

    |block1 block2 block3|

    block1 := [ self helloWorld ].
    block2 := [:arg1 | self helloWorld ].
    block3 := [:arg1 :arg2 :arg3 | self helloWorld ].
As already mentioned above, blocks can be passed to other code as argument. The collection classes in Smalltalk make heavy use of that, in that they provide an extensive set of enumeration functions, which iterate on the collection arguments, using a block argument which performs the operation. For example, here is a general iterator (which is implemented by every collection class), which enumerates the elements of an array. The array is written as an array-literal (that is a compile-time generated object):
    |myCollection|

    myCollection := #(1 2 3 4 5).
    ...
    myCollection do:[:el | Stdout show:el].
    ...
ignore the code inside the block for a moment - the "show:" message, if sent to a stream-like object will print its argument (el in the above example) on the stream. Look at how the block is passed as an argument of the "do:" message, which is sent to the array-instance (myCollection). The above is also possible in JavaScript, but is relatively unreadable there:
    var myCollection;

    myCollection = [1, 2, 3, 4, 5];.
    ...
    myCollection.forEach( function(el) { Stdout.show(el); };
    ...

Returning through a Block

One very special feature of Smalltalk blocks (which is not even available in JavaScript) is their ability to return from their owning method. If a return statement ("^ expression") is evaluated inside a block, the containing method is returned from (not the block). Thus, the following code searches a collection for the first element for which a filter returns a true value:
    myMethod
	...
	someCollection do:[:el |
	    (aMatchBlock value:el) ifTrue:[^ el]
	].
	...
this corresponds to:
    function myMethod() {
	...
	someCollection.forEach( function(el) {
	    if (aMatchBlock(el)) return el from myMethod;
	};
	...
in Java, you have to simulate that behavior using exceptions, which might look quite complicated. Passing a returnung-block down to some other call corresponds technically to a non-local return, from however deep down that block was passed.


Copyright © 2002 Claus Gittinger, all rights reserved

<info@exept.de>

Doc $Revision: 1.1 $
Last modification: $Date: 2011/12/22 09:28:01 $