Skip to content

For Comprehensions Explained

johnmcclean-aol edited this page Nov 21, 2016 · 9 revisions

Cyclops has merged with simple-react. Please update your bookmarks (stars :) ) to https://github.com/aol/cyclops-react

All new develpoment on cyclops occurs in cyclops-react. Older modules are still available in maven central.

screen shot 2016-02-22 at 8 44 42 pm

for comprehensions explained

For comprehensions are useful for iterating over nested structures (e.g. collections, Streams, Optionals, CompletableFutures or other Monads).

Given a list of Strings

 List<String> list = Arrays.asList("hello","world","3");

We can iterate over them using Java 5 'foreach' syntax

 for(String element : list){
 	System.out.println(element);
 }

The equivalent for comprehension would be

Do.add(list)
  .yield( element ->  element )
  .forEach(System.out::println);  

We have simply converted the list to a Stream and are using Stream forEach to iterate over it.

But.. if we nest our looping

  List<Integer> numbers = Arrays.asList(1,2,3,4);

  for(String element : list){
     for(Integer num : numbers){
 		System.out.println(element + num);
 	  }
  }                              

Things start to become a little unwieldy, but a little less so with for comprehensions

Do.add(list)
  .with(element -> numbers)
  .yield(  element -> num  -> element + num )
  .forEach(System.out::println);

Let's add a third level of nesting

List<Date> dates = Arrays.asList(new Date(),new Date(0));

for(String element : list){
     for(Integer num : numbers){
    	 for(Date date : dates){
 			System.out.println(element + num + ":" + date);
 	 	 }
 		
 	  }
  }

And the for comprehension looks like

Do.add(list)
  .add(numbers)
  .add(dates)
  .yield( element ->  num ->  date -> element + num+":"+date )
  .forEach(System.out::println);

Stream map

 list.stream()
     .map(element -> element.toUpperCase())
     .collect(Collectors.toList());

Can be written as

  ForComprehensions.foreach1(c -> c.mapAs$1(list))
                                   .yield( (Vars1<String> v) -> c.$1().toUpperCase())
                    .collect(Collectors.toList());

Mixing types

Running a for comprehension over a list (stream) and an Optional

	List<String> strs = Arrays.asList("hello","world");
	Optional<String> opt = Optional.of("cool");
	
	
	Do.add(strs)
      .add(opt)
      .yield(v1->v2 -> v1 + v2)
      .forEach(System.out::println);

Outputs : [hellocool, worldcool]

Or the other way around

  	List<String> strs strs = Arrays.asList("hello","world");
	Optional<String> opt = Optional.of("cool");
	
	Do.add(opt)
	  .add(strs)
	  .yield(v1->v2 -> v1+ v2)
	  .<String>toSequence()
	  .forEach(System.out::println);
	
	assertThat(results.get(),hasItem("coolhello"));
	assertThat(results.get(),hasItem("coolworld"));

Outputs : coolhello],[coolworld

The first type used controls the interaction!

Visualisation of CompletableFuture / Stream mixed combinations

CompletableFuture defined first do - completablefuture and stream

Stream defined first do - stream and completablefuture

Filtering

Guards (filter commands) can be placed at any stage of a for comprehension. E.g.

             Stream<Double> s = Do.with(Arrays.asList(10.00,5.00,100.30))
					.and((Double d)->Arrays.asList(2.0))
					.filter((Double d)-> (Double e) -> e*d>10.00)
					.yield((Double base)->(Double bonus)-> base*(1.0+bonus));
	
	double total = s.collect(Collectors.summingDouble(t->t));
	assertThat(total,equalTo(330.9));

For more info

Clone this wiki locally