Exploring the Smalltalk Collections API in unit tests using Pharo 8.0
Learn a little Smalltalk, and you might thank yourself
later.
A Little Smalltalk for the Holidays
I wanted to write a blog for the holidays to meet my commitment of writing one blog per month. I had some experiments I had written using Pharo 8.0 that I decided to organize into individual tests so I could blog about them. The experiments were a Smalltalk port of the methods I had written about in the following blog about Eclipse Collections.
The Smalltalk Collections API is amazing. I didn’t realize how amazing the Smalltalk Collections framework was until I started programming in Java. The absence of a rich collections API made me long for Smalltalk in my early days as a Java programmer. Smalltalk can help you become a better programmer in any programming language you code in. At least, this has been my personal experience. Learning Smalltalk in 1994 prepared me for the eventual inclusion of lambdas and Streams in Java, nearly twenty years later when they were released in Java 8 (2014).
Learn Smalltalk in Y minutes
For a quick intro to Smalltalk syntax and methods, check out the following link.
Syntax used in the examples
In the examples in this blog I will use temporary variables ( | variable1 variable2 |
), assignment operator (:=
), statement ending (.
), unary messages (e.g. new
, isEmpty
, asBag
) , binary messages (e.g. ,
and =
), keyword messages (e.g. with:
, select:
, inject:into:
, assert:equals:
, etc.), a reserved word (self
), String literals (e.g. ‘banana’
), Character literals (e.g. $a
, $b
, $c
), SmallInteger
literals (e.g. 5
, 3
, 2
), and code blocks([ :parameter | expression ]
). Code blocks are the equivalent of lambdas in Java, and are full lexical closures that have a single type in Smalltalk (BlockClosure
in Pharo).
Object-Oriented Basics
This is what it looks like creating temporary variables, instantiating objects and calling methods on objects in both Java and Smalltalk with assignment.
Java:
Object param1, param2, param3;
SomeClass object = new SomeClass();
Object result1 = object.method();
Object result2 = object.withOne(param1);
Object result3 = object.withOneTwo(param1, param2);
Object result4 = object.withOneTwoThree(param1, param2, param3);
Smalltalk:
| object param1 param2 param3 result1 result2 result3 result4 |
object := SomeClass new.
result1 := object method.
result2 := object withOne: param1.
result3 := object withOne: param1 two: param2.
result4 := object withOne: param1 two: param2 three: param3.
Smalltalk Collection methods covered in this blog
The following are the iteration patterns I will cover in this blog. I decided to keep it limited to a set of basic patterns. The full source for all examples is here.
select:
reject:
collect:
flatCollect:
detect:
/detect:ifNone:
inject:into:
groupedBy:
anySatisfy:
/allSatisfy:
/noneSatisfy:
count:
The following are additional methods I used in the tests.
,
=
asBag
assert:
/assert:equals:
/deny:
asUppercase
first
includes:
ifTrue:ifFalse:
isEmpty
newFromKeys:andValues:
occurrencesOf:
with:
The following are the data structures I used in the tests.
Bag
OrderedCollection
OrderedDictionary
Set
Smalltalk Iteration Patterns
Select
The method select:
can be used to a filter a collection inclusively. The method takes a one-argument block that returns a Boolean
(aka Predicate
).
Reject
The method reject:
can be used to a filter a collection exclusively. The method takes a one-argument block that returns a Boolean
(aka Predicate
).
Collect
The method collect:
can be used to transform a collection. The method takes a one-argument block that returns any type (aka Function
).
FlatCollect
The method flatCollect:
can be used to flatten and transform a collection. The method takes a one-argument block that returns a Collection
(aka Function
). In Smalltalk, a String
is a Collection
. The flatCollect:
method on a String
flattens the Character
instances into a Collection
. I convert the characters to a Bag
using asBag
so I can use the occurrencesOf:
method to test the count of each character.
Detect / DetectIfNone
The methods detect:
and detect:ifNone:
can be used to find the first object that matches a condition. The detect:
method takes a one-argument block that returns a Boolean
(aka Predicate
). The detect:ifNone:
method additionally takes a second zero-argument block as a second parameter that is evaluated and the result returned in the case that no element matches the condition of the first block.
InjectInto
The method inject:into:
injects an initial value into an iteration over a Collection
, and some result of each iteration is injected into the next iteration and so on and so forth. This method is usually used to implement methods like sum
, min
, max
, etc. The method inject:into:
is one of the most general, flexible and powerful of the iteration patterns in Smalltalk. In the following example, I inject an empty String
into a iteration, and the result of each iteration is a concatenation of the injected String
with a comma and each element. The only element that is not concatenated with a comma is the first one.
GroupedBy
The method groupedBy:
iterates over a Collection
and groups the elements in a Dictionary
containing collections of the same type where the keys are derived by applying a function to each element. The groupedBy:
method takes a one-argument block that returns any type (aka Function
).
AnySatisfy / AllSatisfy / NoneSatisfy
The methods anySatisfy:
, allSatisfy:
and noneSatisfy:
test a collection to see if a certain condition is met and returns a Boolean
result. All three methods take a one-argument block as a parameter. The block returns a Boolean
(aka Predicate
). Each of these methods will short-circuit if the Boolean
result of the Predicate
is the opposite of what is expected for the method.
Count
The method count:
returns the count of the number of elements that match a given condition. The count:
method takes a one-argument block that returns a Boolean
(aka Predicate
).
The Source
Here’s a link to a gist with the source code in the examples above. Enjoy!
That’s all folks!
Thank you for reading! I hope you enjoyed my trip down Smalltalk memory lane. If you’re bored this holiday season with everything getting locked down due to the pandemic, then maybe give yourself the gift of a little Smalltalk learning. The syntax will be different than what you are used to, but if you learn it, you will understand why Smalltalk developers prefer it to curly brace and parentheses languages.
I hope you have a safe, happy and healthy holiday! Hug your loved ones, take care of your family and yourself.
I am a Project Lead and Committer for the Eclipse Collections OSS project at the Eclipse Foundation. Eclipse Collections is open for contributions. If you like the library, you can let us know by starring it on GitHub.