Exploring the Smalltalk Collections API in unit tests using Pharo 8.0

Donald Raab
6 min readDec 27, 2021

Learn a little Smalltalk, and you might thank yourself later.

https://pharo.org/

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.

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).

Filtering fruit using the select: method

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).

Filtering fruit using the reject: method

Collect

The method collect: can be used to transform a collection. The method takes a one-argument block that returns any type (aka Function).

Transforming fruit using the collect: method

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.

Flattening the characters in fruit using the flatCollect: method

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.

Finding the fruit that starts with a letter using detect: and detect:ifNone:

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.

Concatenating the fruit in a collection using inject:into:

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).

The method groupedBy: used to group fruit by the first character

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.

Examples of the method anySatisfy:
Examples of the method allSatisfy:
Examples of the method noneSatisfy:

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).

Count all the bananas and fruit that contains the letter $a

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.

--

--

Donald Raab

Java Champion. Creator of the Eclipse Collections OSS Java library (http://www.eclipse.org/collections/). Inspired by Smalltalk. Opinions are my own.