Starting out with Smalltalk can be a little jarring as it doesn’t have the similar syntax as launguages that
are more heavily inspired by C. Dictionaries are one kind of data structure where I noticed this the most so
I put together my notes on using them in Pharo with some comparisons to Python. In many other languages there
is a subscript operator that allows you to access a value in a dictionary
(and also a position in an array). It’s often written dict[key]
to access a value and dict[key]=val
to
set a value to an existing key or add in a new key. Smalltalk doesn’t contain
a subscript operator and so interacting with dictionaries requires a slight change
in the mental model to get things done.
Dictionaries in Pharo are composed of Associations. An Association is a class that holds a single key–value
pair. You can create an Association by sending the ->
binary message to an object (e.g. 1 -> 2
). Associations
can be used outside of dictionaries as well, for example the following code snippet creates an Array of Associations:
{ 1 -> 2. 3 -> 4. 5 -> 6 }
You can create a Dictionary from an Array of Associations using the newFrom:
message
Dictionary newFrom: { 1 -> 2. 3 -> 4. 5 -> 6 }.
or you can achieve the same thing with the at:put:
message:
Dictionary new
at: 1 put: 2;
at: 3 put: 4;
at: 5 put: 6;
yourself.
While the newFrom:
message seems intuative to me, the at:put:
way of creating Dictionaries took me
a while to get my head around. First,
notice the semicolons between the successive calls to at:put:
, this is for cascading messages that should
all go to the same “receiver”, which in this case is the Dictionary object created by new
. What happens
if you don’t put in the semicolons? Pharo will get confused and think you are trying to send a single
message called at:put:at:put:at:put:
instead of three separate messages and you’ll get an error.
Why is there no semicolon after
new
? So what’s happening is that the new
message is creating an instance of the Dictionary class i.e.
a Dictionary object which gets returned from the new
message. The at:put:
messages are then applied to
the return value of new
. If you put a semicolon after new
then all of the subsequent messages will be
sent to what new
was sent to, rather than the result of new
. In other words, at:put:
will be sent to
the Dictionary class and not a Dictionary object (which causes an error). The last bit of the statement, yourself
,
is needed to return the Dictionary object. Without yourself
the return value of the final at:put:
message
is used, which is the value added to the dictionary. If the return value of at:put:
was the dictionary
object then the yourself
message wouldn’t be needed at all.
The basic method of getting values is to use the at:
message:
d := Dictionary newFrom: { 1 -> 2. 3 -> 4. 5 -> 6 }.
d at: 3. ">>> 4"
Just like in Python, if you try to access a key that doesn’t exist you’ll get an error
d at: 100. "Error KeyNotFound"
but unlike Python there are multiple ways to avoid this error using variants of the at:
message.
The at:update:initial:
message allows you either update or set a value in a dictionary:
d at: 1 update: [ :value | value + 1 ] initial: [ 1 ].
which is broadly equivelent to the following Python code
try:
d[1] += 1
except KeyError:
d[1] = 1
There are many other variants of at:
that modify the behaviour depending on whether the key is present
or not.
Enumerating in Pharo Smalltalk can also be achieved in multiple ways. The do:
message is available in many
classes for iterating, which for Dictionaries iterates through the values:
d do: [ :value | Transcript show: value; cr]
which is Python would be:
for value in d.values():
print(value)
The do:
message is an alias for valuesDo:
and there is also a keysDo:
for iterating through the keys
and associationsDo:
for iterating through the key–value pairs. Unlike Python which returns a tuple
of the key and value, in Pharo an Association object is returned. This class responds to the key
and value
messages for accessing each.
d associationsDo: [ :pair | Transcript show: pair key; cr; show: pair value; cr]
and the Python equivelent:
for pair in d.items():
print(pair[0])
print(pair[1])
Despite the differences introduced through the syntax of each language, using dictionaries in Pharo Smalltalk and Python are basically the same.