Note: The following is implementation dependent. The ANSI Smalltalk Standard defines:
It is unspecified whether the values of identical literals are the same or distinct objects. It is also unspecified whether the values of separate evaluations of a particular literal are the same or distinct objects.
That is you cannot rely on two (equal) literals being the same or being different whatsoever. However, the following is a common implementation
Literal Arrays in Squeak and Pharo
At least in Squeak and Pharo, literal arrays are constructed when saving (= compiling) the method and are stored inside the method object (a CompiledMethod
). This means that changing a literal array changes the value stored in the method object. For example:
MyClass>>example1
| literalArray |
literalArray := #( true ).
literalArray first ifTrue: [
literalArray at: 1 put: false.
^ 1].
^ 2
This method returns 1
only ever on the first invocation:
| o p |
o := MyClass new.
o example1. "==> 1"
o example1. "==> 2"
o example1. "==> 2"
p := MyClass new.
p example1. "==> 2"
This is even independent of the receiver.
But again, you can't rely on that, it might be different in other Smalltalks.
Different approaches
Copying (always safe)
To overcome that, you can simply copy the literal array before use. Your example:
MyClass>>incrementedNumbers
| numbers |
numbers := #( 1 2 3 4 5 6 7 8 ) copy. "<====== "
1 to: numbers size: do: [:index |
numbers at: index put: (numbers at: index) + 1].
^ numbers
This is always safe and will not mutate the array within the method object.
Braced arrays (mostly portable)
While not defined in the standard, most implementations support braced array expressions like this:
{ 1 . 'foo' . 2 + 3 }.
which is equivalent to:
Array with: 1 with: 'foo' with: 2 + 3.
These arrays are constructed at execution time (in contrast to literal arrays) and are hence safe to use. Your example again:
MyClass>>incrementedNumbers
| numbers |
numbers := { 1 . 2 . 3 . 4 . 5 . 6 . 7 . 8 }. "<====== "
1 to: numbers size: do: [:index |
numbers at: index put: (numbers at: index) + 1].
^ numbers
(Ab)using literal arrays
There are sometimes reasons to actually mutate literal arrays (or more generally any method literal, to be frank). For example, if you have static information, like images or binary data that don't change at all but are not always used, but you cannot (for whatever reason) use instance or class variables, you might store the object in a literal array upon first use:
MyClass>>staticInformation
| holder |
holder := #( nil ).
holder first ifNil: [ holder at: 1 put: self generateBinaryData ].
^ holder first
The ifNil:
check will only be true the first time the method is executed, subsequent executions will just return the value that was returned by self generateBinaryData
during the first invocation.
This pattern was used by some frameworks for a while.
However, specifically for binary data, most Smalltalks (including Squeak and Pharo) now support a literal byte array of the form #[ … ]
. The method can then simply be written as
MyClass>>staticInformation
^ #[42 22 4 33 4 33 11 4 33 0 0 0 0
4 33 18 4 33 4 33 9 0 14 4 33 4
33 7 4 33 0 0 9 0 7 0 0 4 33 10
4 33 4 33 7 4 33 0 0 9 0 7 0 0 4
" ... "
33 10 4 33 4 33 17 0 11 0 0 4 33
4 33 0 0 17 0 7 0 0 4 33 13 0]