Idiomatic Javaslang: The For-Comprehension
Rahel Lüthy gave a great example on how to simplify Java code by using Javaslang's For-Comprehension.
The example was introduced by Kelsey Gilmore-Innis, Idiomatic Scala: The For-Comprehension. Rahel refactored it into a vegetarian variant.
In order to get the dinner we need some ingredients and tools.
class Charcoal {} // ▪️
class Lighter {} // 🕯
class Fire {} // 🔥
class CornCob {} // 🌽
class Dinner {} // 🍽
A grill service helps us preparing our meal.
interface GrillService {
Option<Charcoal> getCharcoal();
Option<Lighter> getLighter();
Option<Fire> lightFire(Charcoal charcoal, Lighter lighter);
Option<CornCob> getCornCob();
Option<Dinner> grill(Fire fire, CornCob cornCob);
}
As always in real-life, for some reason there might something go wrong. This is reflected by the Option type.
For example there are two possible results when we ask our grill service for a lighter: Some(Lighter)
or None
.
Side-note: Javaslang's Option fixes several issues of Java's Optional type:
- Option is Serializable
- Option is a container that can contain null (correctness of map method)
- Option is Iterable
- Option can be pattern matched (Some and None)
A typical Java implementation of make dinner would look like this:
public static Option<Dinner> makeDinner(GrillService service) {
Option<Charcoal> charcoalOption = service.getCharcoal();
Option<Lighter> lighterOption = service.getLighter();
if (charcoalOption.isDefined() && lighterOption.isDefined()) {
Option<Fire> fireOption = service.lightFire(
charcoalOption.get(), lighterOption.get());
Option<CornCob> cornCobOption = service.getCornCob();
if (fireOption.isDefined() && cornCobOption.isDefined()) {
return service.grill(fireOption.get(),
cornCobOption.get());
}
}
return Option.none();
}
We achieve the same by using Javaslang's For-comprehension:
public static Option<Dinner> makeDinner(GrillService service) {
return
For(service.getCharcoal(), charcoal ->
For(service.getLighter(), lighter ->
For(service.lightFire(charcoal, lighter), fire ->
For(service.getCornCob(), cornCob ->
For(service.grill(fire, cornCob))
.yield()
)
)
)
).toOption();
}
The For expression returns a lazy view of the yielded elements, here a Iterator<Dinner>
.
The Iterator returned by For is also a Javaslang type. It can be converted to most other Javaslang types. Here we convert it to an Option to get the final Option<Dinner>
.
I hope you enjoyed the grill example.
- Daniel