Please try to understand them.
It may also be very interesting to read, if you are a smalltalker !
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.
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).
So, the javaScript:
looks in Smalltalk like:
<statement1> ;
<statement2> ;
<statement3> ;
with the last period being optional.
<statement1> .
<statement2> .
<statement3> .
var a, b, c;
...
a = 5;
b = 4;
c = a;
...
becomes in Smalltalk:
|a b c|
...
a := 5.
b := 4.
c := a.
...
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:
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.
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,
becomes in Smalltalk:
var a, b, c;
...
a = b.fooBar(1, 2);
c = a.fooBarBaz(1, 2, 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.
|a b c|
...
a := b foo:1 bar:2.
c := a foo:1 bar:2 baz:a.
...
Notice, that it is possible in JavaScript, to call a function with a wrong number of
arguments:
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.
var a, b, c;
...
a = b.fooBar(1);
c = b.fooBar(1, 2, 3);
...
Sometimes, especially when code is generated automatically from Java/JavaScript like languages,
methods use a single underscore character to separate arguments, as in:
But still: do not forget that "foo:" and "foo:_:" and "foo:_:_:" are three different
names for three different methods.
x.someGeneratedName:arg1 _:arg2 _:arg3.
Also notice that "foo:bar:" is a different name from "bar:foo:".
(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.)
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:
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):
|block1 block2 block3|
block1 := [ self helloWorld ].
block2 := [:arg1 | self helloWorld ].
block3 := [:arg1 :arg2 :arg3 | self helloWorld ].
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:
|myCollection|
myCollection := #(1 2 3 4 5).
...
myCollection do:[:el | Stdout show:el].
...
var myCollection;
myCollection = [1, 2, 3, 4, 5];.
...
myCollection.forEach( function(el) { Stdout.show(el); };
...
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>