For

johnmcclean-aol edited this page Dec 6, 2016 · 10 revisions
Clone this wiki locally

For

The For class in cyclops-react provides access to a For Comprehension interpreter, with a set of builders For comprehensions for Publishers, Values and access to the lower level Do builder for other types (iterable, reader, future, optional, Stream anyM).

More efficient type specific For Comprehensions are available directly on cyclops-react types via forEach2, forEach3, forEach4 etc.

For allows us to simplify deeply nested map / flatMap operations. One specialization that this covers is the simplification of nested loops (as we can loop through every element in two lists by flatMapping the first list, and mapping over the second list inside that flatMap operation). But For Comprehensions can be used everywhere you find yourself nesting flatMap and map operations.

 Examples

Example the functional elvis operator

Imagine we have a Person class

class Person{
   public String getName();
   public Maybe<Address> getAddress();
}
class Address{
  public Maybe<ZipCode> getZip();
}

If we want to extract the persons name and zip, but only where the zip is present we could make use of Maybe#flatMap. With for comprehensions this becomes neater

import static com.aol.cyclops.control.For.Values.each2;

String nameAndZip = each2(person::getAddress,
                          Address:getZip,
                          (person,zip)-> "Name:" + person.getName()
                                                 + "\nzip" + zip)
                          .orElse("N/A");

Example : merging Streams

We can perform a For comprehension with a Filter over a Flux type (from Pivotal's Reactor project) and a cyclops-react ReactiveSeq. This will visit every element of both Streams. In this example we will filter out any combination where the total is less than 10.

import static com.aol.cyclops.control.For.Publishers.each2;

ListX<Tuple2<Integer,Integer>>  list;


list = each2(Flux.range(1,10), 
             i-> ReactiveSeq.iterate(i,a->a+1)
                            .limit(10),
             (a,b)->a+b<10,
             Tuple::tuple)
       .toListX();

Ultimately this is syntax sugar for the code below, where we perform a nested map operation inside a flatMap operation.

ListX<Tuple2<Integer,Integer>>  list2;  
list2 = Flux.range(1,10)
            .flatMap(i-> ReactiveSeq.iterate(i,a->a+1)
                                    .limit(10)
                                    .filter(a->i+a<10)
                                    .map(a->Tuple.tuple(i,a)))
             .toList()
             .block();

assertThat(list,equalTo(list2));

Example : Flux, ReactiveSeq, Maybe and Mono via For.Publishers

With For.Publishers we can execute For comprehensions, that perform nested loops over any Publisher type.

import org.jooq.lambda.tuple.Tuple;
import org.jooq.lambda.tuple.Tuple4;
import static com.aol.cyclops.control.For.Publishers.each4;

ListX<Tuple4<Integer,Integer,Integer,Integer>>  list;

list = each4(Flux.range(1,10), 
             a-> ReactiveSeq.iterate(a,i->i+1).limit(10),
             (a,b) -> Maybe.<Integer>of(a+b),
             (a,b,c) -> Mono.<Integer>just(a+b+c),
             Tuple::tuple)
            .toListX();

The return type of For operations is a monad wrapper AnyM for more details on how to use AnyM and extract it's values see this page AnyM intro.