Comparing my Smalltalk and Eclipse Collections Wordle Kata Solutions

Donald Raab
10 min readFeb 28, 2022

--

Solving a problem with multiple languages and libraries can sometimes bring new insights.

Challenge Yourself and Others to Learn

I accepted a JLDD (Jet Lag Driven Development) challenge recently to implement a Wordle Checker Kata in Java. My friend José Paumard sent me this challenge and I set out to solve it using my favorite Java collections framework — Eclipse Collections. I wrote a blog about the challenge and my Java / Eclipse Collections solution.

José then published his response in a great JEP Café tutorial video which he tweeted about.

I really enjoyed the video and learned an approach to solving this problem using pattern matching that I wouldn’t have thought of on my own.

I had come up with three slight variations on the same solution using Java with Eclipse Collections, and thought it might be interesting to try and build a solution using Pharo Smalltalk. So of course I built one, and tweeted about it.

Smalltalkers will hopefully recognize the emoji picture I used. It is the basic syntax of a lambda in Smalltalk — [|]. Parameters go on the left side of the pipe, and statements/expressions go on the right.

A few days after posting this, I posted a slight variation on the Smalltalk solution. I have included both Smalltalk solutions in the blog along with three Java / Eclipse Collections solutions.

My Smalltalk Solutions

I used Pharo 8.0 to implement my Smalltalk solution. First, I wrote the set of test assertions in a WordleTest class. Each assertion creates an instance of a Wordle class with a hidden word, and then calls a method named guess: which will output the expected result of the two words matched.

SUnit Test assertions for the Smalltalk version of the Wordle Checker Kata
SUnit Test assertions for the Smalltalk version of the Wordle Checker Kata

I created a Wordle class with an instance variable named hidden, and then added a method named guess: which takes a String parameter.

As you read my Smalltalk solutions, I would note the following. Smalltalk has a very simple and powerful programming paradigm which leads to a very minimal syntax with only five reserved words (true, false, nil, self, super) in the language. There are no control statements (if, for, while) in Smalltalk , and everything is accomplished by sending messages to objects. The equivalent of if statements, for loops, while loops, etc. in other languages are accomplished by sending messages to different objects. Much of this is accomplished by passing lambdas around to methods as parameters, or calling appropriate methods on lambdas.

Smalltalk Solution #1

I wrote my first Smalltalk solution for guess: using with:do: and with:collect: methods. This solution passes all of the tests above.

Smalltalk implementation of the guess method on a Wordle class
Smalltalk implementation of the guess method on a Wordle class

The with:do: and with:collect: methods are available on the SequenceableCollection class in Smalltalk. In Smalltalk, a String is a SequenceableCollection. The following is a view of the Smalltalk Collection hierarchy.

Smalltalk Collection hierarchy

The method named with:do: lets you iterate over two String instances at the same time. The first argument passed is the the String that you want to iterate over with this String, and the second argument is a two argument lambda which doesn’t return anything (equivalent of a Procedure2 or BiConsumer in Java). This let me compare letters at the same index, exclude all of those that were equal and add the remaining letters to the Bag named remaining, which I initialized on the first line of the method. For anyone unfamiliar with the Bag data structure, a Bag is an unordered collection that allows duplicates. Nikhil Nanivadekar wrote a blog about the Bag data structure in Eclipse Collections which explains how it is implemented.

The second method, with:collect:, is similar to with:do: in that it iterates over both String instances together. The difference is that the two argument lambda that is passed as the second argument is used as a Function2 (a two argument Function or BiFunction in Java) to transform the characters to a new String. The transformation will result in either an uppercase letter, a lowercase letter or a “.” being returned for each combination of letters.

The ifTrue:ifFalse: method is a method on the Boolean class in Smalltalk and is overridden by the True and False subclasses. The method takes two lambdas as arguments. Depending on whether the Boolean is true or false, one of the two lambdas will be evaluated. The = method tests for equality and will return true or false. The literal true is the single instance of the class True , and false is an instance of False. I always thought it was kind of clever how Smalltalk implemented if control flow logic without using statements.

Finally, the remove:ifAbsent: method is available on most Collection classes in Smalltalk, and in this instance I am calling this method on a Bag. The result of the method is to return the object that is removed, or if the object is not in the collection, evaluate and return the result of the lambda.

Smalltalk Solution #2

For the second Smalltalk solution, I had hoped to find a method named with:reject:, but unfortunately, no such method exists today. I could have added the myself, but instead I decided to use a method that was already implemented. I used withIndexSelect: as a somewhat more awkward alternative to with:reject: but at least more terse alternative to using with:do:. I also formatted the code for with:collect: using the recommended auto-formatting in Pharo. It should make it easier to read and is a good way to break up the ifTrue:ifFalse: message send with the nested lambdas.

Smalltalk implementation of the guessWithIndex method on a Wordle class

The withIndexSelect: method is passed each character in the hidden String along with its corresponding index in the String. Indexes in Smalltalk are one based, so start at 1, not 0. This can’t be seen in this particular code example, but I think it’s an interesting difference between Smalltalk and Java. I look up the character at the same index position in the guess String using the at: method. Because I am using select: (inclusive filter) and not reject:(exclusive filter), I have to negate the comparison. I use ~= to compare the two letters, which translates to “not equal” in Smalltalk. Finally, I convert the resulting String instance from the call to withIndexSelect: to a mutable Bag. The rest of the solution is identical to my first Smalltalk solution.

Update: May 10, 2022

I upgraded my Pharo IDE to version 10.0. I also refactored my test cases to use a shared data set. This reduced a lot of duplication, and made the test cases much more pleasant to read.

I put the shared test data in a method named hiddenGuessExpectedData.

Two dimensional array containing the test data and expected results

I renamed the test methods to testGuess and testGuessWithIndexSelect.

The testGuess method
The testGuessWithIndexSelect method

A neat side effect of renaming the test methods to better match the name of the methods on the class, is the class now displays green spheres next to each method and allow you to run the tests for the methods in the class itself.

Running tests for methods from the domain class

My Eclipse Collections Solutions

All of my Java solutions used features of the Eclipse Collections library. All three solutions used Strings.asChars(string) to provide access to the primitive collection API. In Eclipse Collections, this static method returns a CharAdapter instance wrapping the String.

Two of the three Eclipse Collections solutions use methods that end in WithIndex. The third solution uses a method named zipChar which allows two primitive collections to be zipped together into char pairs. The basic strategy is pretty much the same between my three solutions, with minor differences in implementation.

  • Create a Bag of characters that do not have direct positional matches between both String instances (hidden and guess)
  • Collect up one of three possible states for each character as a result String. Convert exact positional letter matches to uppercase, letter match in different position to lowercase, and no letter match to a “.”

Eclipse Collections Solution # 1

The first solution I wrote using injectIntoWithIndex and collectWithIndex. I wrote the original Wordle JLDD Kata Challenge blog with this solution included. You can read the blog for more details on the solution.

Eclipse Collections solution using injectIntoWithIndex and collectWithIndex

The thing that bothered me most about this solution is that Eclipse Collections did not yet have rejectWithIndex available on primitive collections. The solution using injectIntoWithIndex is really implementing the rejectWithIndex pattern.

Eclipse Collections Solution #2

I have recently contributed selectWithIndex and rejectWithIndex methods to primitive collections in Eclipse Collections. The same methods existed on the object collection API in Eclipse Collections since the 11.0 release. I used rejectWithIndex in my second solution, along with the collectWithIndex approach used in Eclipse Collections Solution #1.

Eclipse Collections solution using rejectWithIndex and collectWithIndex

The rejectWithIndex method here takes a target collection to store the results in.

Eclipse Collections Solution #3

My third Eclipse Collections solution using zipChar and collectChar was inspired by an alternative zipChar solution written by Vladimir Zakharov.

The method zipChar creates a List of character pairs between both String instances. I reject the exact positional matches between char pairs and collect those char values into a MutableCharBag. Then I collect char values into a String based on the algorithm described above.

From Wordle Kata Solutions to EC Integration Test

The Eclipse Collections Wordle Kata solutions I came up with are good examples of using some of the String iteration patterns available in the library. Because I am leveraging multiple String iteration patterns, I thought it made sense to turn the kata solutions into an integration level test for Eclipse Collections. I committed a new WordleTest class into Eclipse Collections along with my contribution of the primitive versions of selectWithIndex and rejectWithIndex. Below is the source code for the test.

Comparing the solutions

Here is a visualization of the intersection and set of differences between the methods available in Smalltalk and Eclipse Collections to solve this particular kata challenge.

Intersection and Differences between Smalltalk and Eclipse Collections methods

I quite liked the with:do: and with:collect: methods in Smalltalk. I would have preferred to have a with:reject: method for this particular use case. While I could have added this method on my own to SequenceableCollection, it would be nice if it was part of the standard library. However, I doubt that this particular use case will be incentive enough to have it added. The following are the methods using with: as a prefix that are available on SequenceableCollection.

It was interesting to see that there is a withIndexSelect: method in Smalltalk which is a slight difference in name to the equivalent of the selectWithIndex method in Eclipse Collections. There is no equivalent of rejectWithIndex, which is my preference in this use case. The methods collectWithIndex: and withIndexCollect: both exist in Pharo Smalltalk and are synonyms. It appears the naming preference is now in favor of withIndex as a prefix for these methods.

The preference for withIndexCollect: over collectWithIndex: in Pharo Smalltalk

The method zipChar in Eclipse Collections has no equivalent in Pharo Smalltalk today. There is also no equivalent of asLazy either. Most iteration methods on the Smalltalk Collection classes are eager.

In terms of elegance, I think the with:do and with:collect: methods worked the best for this use case. Again, it would have been slightly simpler and clearer if there was a with:reject: method.

In terms of flexibility, I think the withIndex solutions are better than both the other solutions. Since you have the index, you can do whatever you need with it. For example, this would allow you to iterate over as many collections simultaneously as you need.

In terms of fluency and ease of writing, I really liked the asLazy plus zipChar solution which could then just use reject, collectChar and toBag.

Here’s a link to the tweet where Vladimir Zakharov shared his zipChar solution using Eclipse Collections.

Summary

Learning multiple programming languages and libraries and using them to solve various coding katas can help you improve as a programmer. I think it’s interesting to see how languages and libraries compare and how they evolve over time. I don’t recall the methods I used in Pharo Smalltalk being available in VisualAge for Smalltalk when I programmed in it in the 1990s. This is a good sign, as it means the language and core libraries are still evolving. Similarly, the Eclipse Collections library continues to evolve as Java does, and new methods like selectWithIndex and rejectWithIndex now available on both object and primitive collections are a good example of this.

Thank you for reading! I hope you enjoyed this Wordle JLDD Kata Challenge comparison with Smalltalk and Eclipse Collections.

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
Donald Raab

Written by Donald Raab

Java Champion. Creator of the Eclipse Collections OSS Java library (https://github.com/eclipse/eclipse-collections). Inspired by Smalltalk. Opinions are my own.

No responses yet