Skip to content

X For comprehensions

John McClean edited this page Jul 5, 2018 · 4 revisions

For comprehensions in Cyclops X

For comprehensions are a handy short cut for combining nested flatMap operations. For example we can sequence nested calls to methods that return a type that supports flatMap such as Option, Try, Stream, IO and flatten out the result to a single Option, Try, Stream or IO.

E.g. imagine a car parking system that attaches a monthly membership fee to each user (depending on selected grade) and potential costs per use to each of the users registered cars (again depending on grade). A typical business class may have methods something like this

public Option<User> load(long id);
public Option<List<Car>> extractCarDetails(User user);
public Option<BigDecimal> totalBalance(User user,List<Car> cars);

Chaining calls together to calculate a users total outstanding balance would involve messy nested flatMaps.

Option<BigDecimal> balance = load(100L).flatMap(user->extractCarDetails(user)
                                       .flatMap(cars->totalBalance(user,cars));

This can be rewritten, in a cleaner form using for comprehensions

Option<BigDecimal> balance = load(100L).forEach2(this::extractCarDetails,this::totalBalance);

For Comprehension syntax

Direct Approach

Most types in cyclops X with a flatMap method will offer forEach2/3/4 methods directly on the type, as syntax sugar for nested flatMap operations. Types that support this form of Operation include

  1. Monad Types in cyclops.control (Eval, Future, Either, Ior, Try, Option, Maybe, LazyEither/3/4/5, Trampoline)
  2. Persistent collections (cyclops.data)
  3. Types that implemented IterableX
  4. ReactiveSeq

the forEach methods accept Functions of increasing arity (Function, BiFunction, Function3, Function4) that expose each of the current values on the stack.

Comprehensions inner class

An alternative mechanism is provided on some classes via an internal inner class called Comprehensions. The Comprehensions inner class consists of a larger number of overloaded forEach methods. Each method accepts a Function with a Tuple of increasing arity. In some cases this may be cleaner to use than the direct forEach methods - particularly where all available values are not needed in each subsequent computation, or where the methods being called accept Tuples as input parameters.

Types in the core of Cyclops that support the Comprehensions inner class approach are

  1. IO
  2. Managed
  3. Unrestricted
  4. Option
  5. Maybe
  6. Eval
  7. Unrestricted

Types in Cyclops pure that support the Comprehensions inner class approach are

  1. Identity
  2. Free
  3. Active
  4. Nested

The example using Option above rewritten using the inner class approach looks like this

Option<BigDecimal> balance = Comprehensions.forEach(load(100L),
                                                    this::extractCarDetails,
                                                    t2->totalBalance(t2._1(),t2._2());

Further Reading

Clone this wiki locally