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