Skip to content

Old cyclops modules docs

johnmcclean-aol edited this page Nov 23, 2016 · 1 revision

Need organized.

The Decomposable Interface / Trait

The Decomposable Interface defines an unapply method that is used to convert the implementing Object into an iterable. This can be used to control how Cyclops performs recursive decomposition.

    public <I extends Iterable<?>> I unapply();

Interfaces that extend Decomposable

  • ValueObject
  • StreamableValue
  • CachedValues, PTuple1-8

Coercing any Object to a Decomposable

    As.asDecomposable(myObject).unapply().forEach(System.out::println);

com.aol.cyclops.dynamic.As provides a range of methods to dynamically convert types

cyclops-monad-api

  • AnyM : Abstraction over any Java monad
  • OptionalT : Optional Monad Transformer
  • StreamT : Stream Monad Transformer
  • StreamableT : Streamable Monad Transformer
  • ListT : List Monad Transformer
  • CompletableFutureT : CompletableFutute Monad Transformer

cyclops-monad-api is normally imported via other cyclops modules, as full functioning depends on cyclops-streams. Instructions to import it in standalone format are at the bottom of this page.

An alternative to higher-kinded types for providing a common interface over classes that define map / flatMap / filter (etc) methods or their equivalent.

Works by either using a registered 'Comprehender' that handles the actual method invocations, or by taking advantage of InvokeDynamic facility introduced in Java 7 that can make the performance of a dynamic method call almost equivalent to a static call.

Docs

Introducing the cyclops monad api

screen shot 2015-07-22 at 10 19 24 pm

AnyM

Operators

  • aggregate
  • applyM
  • asSequence
  • bind / flatMap - collection, completableFuture, optional, ReactiveSeq, stream, streamable
  • empty
  • filter
  • map
  • reduceM
  • simpleFilter

Examples

Wrap a Stream inside an AnyM, and a Optional inside another, and aggregate their values

List<Integer> result = anyM(Stream.of(1,2,3,4))
                                .aggregate(anyM(Optional.of(5)))
                                .asSequence()
                                .toList();
        
assertThat(result,equalTo(Arrays.asList(1,2,3,4,5)));

AsAnyM / AsAnyMList: factory methods

AnyMonadFunctions

  • liftM : Lift a Function to a function that accepts and returns any monad type (via AnyM).
  • liftM2 : Lift a BiFunction to a function that accepts and returns any monad type (via AnyM).
  • sequence : Convert a collection of monads, to a single monad with a collection
  • traverse : Convert a collection of Monads to a single Monad with a wrapped Collection applying the supplied function in the process

Examples

Dependencies

cyclops-invokedynamic cyclops-sequence-api

Recommended in conjunction with

cyclops-streams

Monoids

Fit the Stream.reduce signature. Can be used to wrap any Monoid type (e.g. functional java).

    Monoid.of("",(a,b)->a+b).reduce(Stream.of("a","b","c"));

Produces "abc"

    fj.Monoid m = fj.Monoid.monoid((Integer a) -> (Integer b) -> a+b,0);
    Monoid<Integer> sum = As.asMonoid(m);
        
    assertThat(sum.reduce(Stream.of(1,2,3)),equalTo(6));

Use in conjunction with Power Tuples or StreamUtils for Multiple simultanous reduction on a Stream.

Coerce to Decomposable

The Decomposable interface specifies an unapply method (with a default implementation) that decomposes an Object into it's elemental parts. It used used in both Cyclops Pattern Matching (for recursively matching against Case classes) and Cyclops for comprehensions (where Decomposables can be lifted to Streams automatically on input - if desired).

     @Test
    public void test() {
        assertThat(AsDecomposable.asDecomposable(new MyCase("key",10))
                .unapply(),equalTo(Arrays.asList("key",10)));
    }
    
    @Value
    static class MyCase { String key; int value;}

Features

Simplify deeply nested looping (over Collections, even Streams, Optionals and more!).

for comprehensions

Docs

Overview of Cyclops For Comprehensions

Two supported formats

  1. Type Do Notation via Do.add / with
  2. Untpyed Do Notation via UntypedDo.add / with

Do Notation

    List<Integer> list= Arrays.asList(1,2,3);
    
    List<Integer> list = Do.add(list)
                                .yield((Integer i)-> i +2)
                                .unwrap();
                
                                        
        
    assertThat(Arrays.asList(3,4,5),equalTo(list));

Yield, Filter and 'and' take curried functions

(That is a chain of single input parameter functions)

        Stream<Integer> stream = Do.add(asList(20,30))
                                   .with( i->asList(1,2,3))
                                   .yield(i-> j -> i + j+2)
                                   .asSequence();

Parameters are stack based, the parameter to the first function is an index into the first Collection or Monad, the parameter to the second function is an index into the second Collection or Monad and so on.

The above code could be rewritten as

        Stream<Integer> stream = Do.add(asList(20,30))
                                   .with(any->asList(1,2,3))
                                   .yield(x-> y -> x + y+2)
                                   .asSequence();

And it would work in exactly the same way

        List<Integer> list= Arrays.asList(1,2,3);
        Stream<Integer> stream = Do.add(list)
                                .filter(a -> a>2)
                                .yield(a-> a +2)
                                .asSequence();
                
                                        
        
        assertThat(Arrays.asList(5),equalTo(stream.collect(Collectors.toList())));

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 )
     .unwrap()
     .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 )
      .unwrap()
      .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)
        .unwrap()
        .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))
                        .asSequence();
        
        double total = s.collect(Collectors.summingDouble(t->t));
        assertThat(total,equalTo(330.9));

Convert any Object to a Monad

Stream conversions

  • Collection to Stream

  • Iterable to Stream

  • Iterator to Stream

  • Array to Stream

  • Int to IntStream.range(int)

  • File to Stream

  • URL to Stream

  • BufferedReader to Stream

  • InputStream to Stream

  • ResultSet to Stream

  • Enum to Stream

  • String to Stream

  • ObjectToStreamConverter

Optional conversions

  • NullToOptionalConverter
  • Optional to Optional

CompletableFuture Conversionss

  • Callable to CompletableFuture
  • Supplier to CompletableFuture

Cyclops Monadic For Comprehensions

Cyclops for comphrensions allow deeply nested iterations or monadic operations to be expressed as a simple foreach expression. The implementation is inspired by the rather excellent Groovy implementation by Mark Perry (Functional Groovy) see Groovy Null Handling Using Bind, Comprehensions and Lift. They will work with any Monad type (JDK, Functional Java, Javaslang, TotallyLazy, Cyclops etc).

The Cyclops implementation is pure Java however, and although it will revert to dynamic execution when it needs to, reflection can be avoided entirely.

Custom interfaces

  • Support for custom interface definition with virtually unlimited nesting
    Stream<Integer> stream = foreachX(Custom.class,  
                                    c-> c.myVar(list)
                                        .yield(()->c.myVar()+3)
                                    );

    Optional<Integer> one = Optional.of(1);
    Optional<Integer> empty = Optional.empty();
    BiFunction<Integer,Integer,Integer> f2 = (a,b) -> a *b; 

For more info

Clone this wiki locally