Haiku Kata using String transform, Text Blocks, and Switch Expressions

Donald Raab
Javarevisited
Published in
7 min readApr 17, 2024

--

I solved the Haiku Kata again using more features added since Java 12

Photo by Jordan Christian on Unsplash

Finding String transform

I found a method on the Java String class I hadn’t seen before. I discovered it while running the String class through my Java class kaleidoscope. There is a method that was added in Java 12 named transform.

One of the views in my Java class kaleidoscope

The transform method makes it possible to pass a Function to a String and have any type returned. In Java, the more popular name for this method is map on Stream and Optional.

I couldn’t understand why this method was added to the String class. This is the only method on String that takes a Functional Interface like Function.

What is the purpose of this method on String?

Instead of scratching my head wondering, I decided to see what I could do with this method.

Haiku Kata

I created a kata for a set of eleven Haiku I wrote during a self-enforced writing detox period I went through in September 2021. I turned my haiku into a Java kata and blogged about it here. José Paumard turned my Haiku Kata into an amazing YouTube tutorial in JEP Café #9.

José’s JEP Café #9 is currently one of the Top 10 most popular videos on the Java YouTube channel based on views. Congrats José!

Paul King also implemented the Haiku Kata in Groovy last year.

The Haiku Kata was intended to be a way for me to experiment with Java’s Text Block feature. Text Blocks were added as a standard feature in Java 15, and were in preview in Java 13 and 14.

In the Haiku Kata, I adapt a String stored as a Text Block into a CharAdapter class from Eclipse Collections. The code I write to do this looks like this.

CharAdapter chars = Strings.asChars("Hello!");

The equivalent using the Java chars method looks like this.

IntStream chars = "Hello!".chars();

With the new transform method on the String class, it is possible to flip this code.

CharAdapter chars = "Hello!".transform(Strings::asChars);

There is something subtle that happens with this inversion of behavior, other than requiring a few more characters. To see the effect, it helps to have bigger String instances than “Hello!”. This is why I went back to the Haiku Kata to try the transform method.

Distinct Letters

One of the first tests to implement in the Haiku Kata is finding the distinct letters in the Tex Block. Using the String transform method, it is possible to inline all of the code to look as follows.

@Test
public void distinctLettersEclipseCollections()
{
String distinctLetters = """
Breaking Through Pavement Wakin' with Bacon Homeward Found
---------------- -------- ----------------- --------------
The wall disappears Beautiful pavement! Wakin' with Bacon House is where I am
As soon as you break through the Imperfect path before me On a Saturday morning Home is where I want to be
Intimidation Thank you for the ride Life’s little pleasures Both may be the same

Winter Slip and Slide Simple Nothings With Deepest Regrets
--------------------- --------------- --------------------
Run up the ladder A simple flower With deepest regrets
Swoosh down the slide in the snow Petals shine vibrant and pure That which you have yet to write
Winter slip and slide Stares into the void At death, won't be wrote

Caffeinated Coding Rituals Finding Solace Curious Cat Eleven
-------------------------- -------------- ----------- ------
I arrange my desk, Floating marshmallows I see something move This is how many
refactor some ugly code, Cocoa brewed hot underneath What it is, I am not sure Haiku I write before I
and drink my coffee. Comfort in a cup Should I pounce or not? Write a new tech blog.
"""
.transform(Strings::asChars)
.select(Character::isAlphabetic)
.collectChar(Character::toLowerCase)
.distinct()
.toString();

Assertions.assertEquals("breakingthoupvmwcdflsy", distinctLetters);
}

The transform method works better here than the original form which would require wrapping the entire text block in a method call to Strings.asChars().

This is what it would have looked like without the transform method.

@Test
public void distinctLettersEclipseCollectionsWithoutTransform()
{
String distinctLetters = Strings.asChars("""
Breaking Through Pavement Wakin' with Bacon Homeward Found
---------------- -------- ----------------- --------------
The wall disappears Beautiful pavement! Wakin' with Bacon House is where I am
As soon as you break through the Imperfect path before me On a Saturday morning Home is where I want to be
Intimidation Thank you for the ride Life’s little pleasures Both may be the same

Winter Slip and Slide Simple Nothings With Deepest Regrets
--------------------- --------------- --------------------
Run up the ladder A simple flower With deepest regrets
Swoosh down the slide in the snow Petals shine vibrant and pure That which you have yet to write
Winter slip and slide Stares into the void At death, won't be wrote

Caffeinated Coding Rituals Finding Solace Curious Cat Eleven
-------------------------- -------------- ----------- ------
I arrange my desk, Floating marshmallows I see something move This is how many
refactor some ugly code, Cocoa brewed hot underneath What it is, I am not sure Haiku I write before I
and drink my coffee. Comfort in a cup Should I pounce or not? Write a new tech blog.
""")
.select(Character::isAlphabetic)
.collectChar(Character::toLowerCase)
.distinct()
.toString();

Assertions.assertEquals("breakingthoupvmwcdflsy", distinctLetters);
}

It is harder to determine what type the select method is being applied to as you have to scan up to the top of the text block to see the String.asChars( opening.

Top Three Letters

The test to find the top three letters in the haiku can be rewritten using a single fluent line of code, by using the Switch Expressions feature of Java to make the test Assertions part of a call to forEachWithindex.

@Test
public void topLettersEclipseCollections()
{
"""
Breaking Through Pavement Wakin' with Bacon Homeward Found
---------------- -------- ----------------- --------------
The wall disappears Beautiful pavement! Wakin' with Bacon House is where I am
As soon as you break through the Imperfect path before me On a Saturday morning Home is where I want to be
Intimidation Thank you for the ride Life’s little pleasures Both may be the same

Winter Slip and Slide Simple Nothings With Deepest Regrets
--------------------- --------------- --------------------
Run up the ladder A simple flower With deepest regrets
Swoosh down the slide in the snow Petals shine vibrant and pure That which you have yet to write
Winter slip and slide Stares into the void At death, won't be wrote

Caffeinated Coding Rituals Finding Solace Curious Cat Eleven
-------------------------- -------------- ----------- ------
I arrange my desk, Floating marshmallows I see something move This is how many
refactor some ugly code, Cocoa brewed hot underneath What it is, I am not sure Haiku I write before I
and drink my coffee. Comfort in a cup Should I pounce or not? Write a new tech blog.
"""
.transform(Strings::asChars)
.select(Character::isAlphabetic)
.collectChar(Character::toLowerCase)
.toBag()
.topOccurrences(3)
.forEachWithIndex((each, index) -> Assertions.assertEquals(switch (index)
{
case 0 -> PrimitiveTuples.pair('e', 94);
case 1 -> PrimitiveTuples.pair('t', 65);
case 2 -> PrimitiveTuples.pair('i', 62);
default -> null;
}, index < 3 ? each : null));
}

The method forEachWithIndex is one of the lesser known methods available in Eclipse Collections. The equivalent Assertion code before required calling Assertions.assertEquals three separate times with an indexed lookup into the resulting ListIterable returned from topOccurrences.

Duplicates and Uniques

There wasn’t much to change in the duplicates and uniques code, other than inlining Text Block in the test and using the String transform method.

@Test
public void duplicatesAndUniqueEclipseCollections()
{
MutableCharBag chars = """
Breaking Through Pavement Wakin' with Bacon Homeward Found
---------------- -------- ----------------- --------------
The wall disappears Beautiful pavement! Wakin' with Bacon House is where I am
As soon as you break through the Imperfect path before me On a Saturday morning Home is where I want to be
Intimidation Thank you for the ride Life’s little pleasures Both may be the same

Winter Slip and Slide Simple Nothings With Deepest Regrets
--------------------- --------------- --------------------
Run up the ladder A simple flower With deepest regrets
Swoosh down the slide in the snow Petals shine vibrant and pure That which you have yet to write
Winter slip and slide Stares into the void At death, won't be wrote

Caffeinated Coding Rituals Finding Solace Curious Cat Eleven
-------------------------- -------------- ----------- ------
I arrange my desk, Floating marshmallows I see something move This is how many
refactor some ugly code, Cocoa brewed hot underneath What it is, I am not sure Haiku I write before I
and drink my coffee. Comfort in a cup Should I pounce or not? Write a new tech blog.
"""
.transform(Strings::asChars)
.select(Character::isAlphabetic)
.collectChar(Character::toLowerCase)
.toBag();

CharBag duplicates = chars.selectDuplicates();
CharSet unique = chars.selectUnique();

Assertions.assertEquals(chars, duplicates);
Assertions.assertEquals(CharSets.immutable.empty(), unique);
}

Final Thoughts

I can’t tell yet if the String transform method has interesting uses that I haven’t thought of yet, but I was happy to see how it worked with the the Haiku Kata. In the original version of the kata, I stored the text block into a field called haiku so as to keep the huge text block out of the methods, taking away from the method calls. The transform method created a nice separation and progression between the inlined String Text Block and the transformation code that found the distinct, top three, duplicate and unique characters.

Thanks for reading, and I hope you enjoyed learning about my recent discovery of the String transform method.

I am the creator of and committer for the Eclipse Collections OSS project, which is managed at the Eclipse Foundation. Eclipse Collections is open for contributions.

--

--

Donald Raab
Javarevisited

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