Powerful, modular extensions for Java 8. Take what you need or want.
- Extensible For Comprehensions
- Pattern Matching
- Advanced Monadic (Stream, Optional etc) cross-type operations
- Powerful Tuple implementation
- Trampoline
- Try
- Enable Switch
- Utils for working with Functions
- Very powerful Stream processing with SequenceM
- Very powerful Stream processing with StreamUtils
- Memoisation
- Introducting the Cyclops Monad API
- Easier Try with Cyclops
- 4 flavors of Java 8 Functions
- Memoise Functions in Java 8
- Strategy Pattern in Java 8
- Pattern Matching in Java 8
- Functional Feature Toggling
- Dependency injection using the Reader Monad in Java8
To import all modules use cyclops-all. For individual modules, see bottom of this page!
Import integration modules individually as needed.
NB Cyclops All includes all Cyclops module except the integration modules (currently cyclops-javaslang).
Cyclops core goal is to raise up Java 8 to a higher level by providing modular, extensible enhancements that will interoperate or aid interoperability with other Java libraries. To do this we make use of Java facilities such as Service Loading for extenisibility and Invoke Dynamic dynamic method calls (when we don't know anything about an external type, but have been asked to handle it). All points of contact with InvokeDynamic code can be replaced by custom extensions if neccessary.
Perform nested operations on Collections or Monads.
Stream<Double> s = Do.add(asList(10.00,5.00,100.30))
.add(asList(2.0))
.with( d -> e ->asList(e*d*10.0))
.yield(i -> j -> k -> i*(1.0+j)*k).unwrap();
double total = s.collect(Collectors.summingDouble(t->t));
Advanced Scala-like pattern matching for Java 8. Match recursively against most Objects / datastructures.
Features include
- Sequential, Parallel and Async execution
- Match by type, value, predicate or Hamcrest Matcher
- Recursively decompose and match against Case classes
- Fluent step builders for common cases
- Fluent, functionally compositional monad-like core Case and Cases classes
- Support for chain of responsibility pattern within a Stream
- Support hamcrest matchers
- Java 8 predicates for matching.
- Match on first (return Optional)
- Match many (return Stream)
- Strict and lose typing
- Pre & post value extraction per case
- Match using multiple in case expressions via tuples or iterables of predicates / matchers
- Match against streams of data
- Usable within a Stream (strategy pattern)
- Fluent step builders
private <I,T> CheckValues<Object, T> cases(CheckValues<I, T> c) {
return c.with(1,2,3).then(i->"hello")
.with(4,5,6).then(i->"goodbye");
}
@Test
public void test(){
assertThat(As.asMatchable(new MyCase(1,2,3)).match(this::cases),equalTo("hello"));
}
flatMap (bind) across Stream and Optional types (null entries are removed)
List<Integer> list = anyM(Stream.of(Arrays.asList(1,3),null))
.flatMapOptional(d-> Optional.ofNullable(d))
.map(i->i.size())
.peek(System.out::println)
.asSequence()
.toList();
assertThat(Arrays.asList(2),equalTo(list));
Lift a File to a Stream
With a file "input.file" that contains two lines
- hello
- world
We can stream the contents like so...
List<String> result = anyM("./input.file")
.liftAndBindFile(File::new)
.asSequence()
.toList();
assertThat(result,equalTo(Arrays.asList("hello","world")));
For multiple files...
List<String> result = anyM("./input.file","./input2.file")
.liftAndBindFile(File::new)
.asSequence()
.toList();
assertThat(result,equalTo(Arrays.asList("hello","world","hello2","world2")));
Features include
- Wrap any Tuple type / Object (mapping fields to elements and back)
- Method call chaining support
- Asyncrhonous method call chaining support
- Inheritance relationship between Tuples
- Lazy and Strict map methods
- Lazy reordering
- Pattern matching
- For comprehensions
- Useful utility methods (asStreamOfStrings, asTwoNumbers etc)
- Concatonation
- LazySwap (reverse) vMemoization
- asCollector
- asReducer
Utilise the heap rather than the Stack for (tail) recursive algorithms in Java.
The Java code below will result in a Stackoverflow error
@Test @Ignore
public void trampolineTest1(){
assertThat(loop1(500000,10),equalTo(446198426));
}
Integer loop1(int times,int sum){
if(times==0)
return sum;
else
return loop1(times-1,sum+times);
}
The same code using Trampoline works fine.
@Test
public void trampolineTest(){
assertThat(loop(500000,10).result(),equalTo(446198426));
}
Trampoline<Integer> loop(int times,int sum){
if(times==0)
return Trampoline.done(sum);
else
return Trampoline.more(()->loop(times-1,sum+times));
}
Cyclops Try is similar to, but functionally different from the Scala (and JAVASLANG) Try monads.
Features
- Try with Resources
- Success and Failure states
- Step builders to guide you through use
- Catch specified (expected) exceptions
- Doesn't operate as a 'catch-all' that may hide bugs
- Recover from different exceptions independently
- Functional composition over both success and failed states
Try.catchExceptions(FileNotFoundException.class,IOException.class)
.init(()->new BufferedReader(new FileReader("file.txt")))
.tryWithResources(this::read)
.onFail(this::recover)
.map(this::continueProcessing)
- Enable / Disable classes (Pattern Match by type)
- convert to Optional or Stream
- standard Java 8 operators (map, flatMap, peek, filter, forEach) + flatten etc
- isEnabled / isDisabled
- Biased towards enabled (right biased).
Switch<Feature> switch = createSwitch(config);
switch.map(this::processData); //if live, data is processed, otherwise nothing happens
- Decomposable : decompose an Object to a Iterable over it's values
- Matchable : add pattern matching capabilities to an Object
- Doable : add for comprehension capabilities to an Object
- Streamable : add repeatable Streaming capabilities
- Mappable : add the ability to coerce an Object to a map
- Printable : ability to println as an expression
- ValueObject : Matchable and Decomposable object
- StreamableValue : Streamable and Doable ValueObject
com.aol.cyclops.dynamic.As offers duck typing / coercion to many different types (including the above traits) and
- com.aol.cyclops.lambda.monads.Monad
- com.aol.cyclops.lambda.monads.Functor
- com.aol.cyclops.lambda.monads.Monoid
- Supplier
- Currying : com.aol.cyclops.functions.Curry
- Currying for Consumers : com.aol.cyclops.functions.CurryConsumer
- Uncurrying : com.aol.cyclops.functions.Uncurry
- Uncurrying for Consumers : com.aol.cyclops.functions.UncurryConsumer
- Type Inferencing help : com.aol.cyclops.lambda.utils.Lambda
- Memoisation : com.aol.cyclops.functions.Memoise
This is a class that helps work around the limitations of Java 8 lambda expressions as closures. In particular the workings of 'effectively final'.
LazyImmutable allows a capture value to be set exactly once
E.g. from cyclops-pattern-matching the code to make an Extractor memoised ->
public static final <T,R > Extractor<T,R> memoised( Extractor<T,R> extractor){
final LazyImmutable<R> value = new LazyImmutable<>();
return input -> {
return value.computeIfAbsent(()->extractor.apply(input));
};
}
computeIfAbsent is used to extract the value from the LazyImmutable, and takes a Supplier as an argument. The Supplier is only invoked once (the first time).
Mutable represents a captured variable inside a Java 8 closure. Because of the effectively final rule we can't access variables from within a Closure, but we can mutate the state of captured Objects. Mutable holds a value we would like mutate (if really, really, neccessary)
Mutable<Integer> timesCalled = Mutable.of(0);
Function<String,String> fn = input -> {
return input + timesCalled.mutate(v -> v+1);
}
- Trampoline pic by Mikefifield Licenced under Creative Commons Attribution 3.0 Unported : https://commons.wikimedia.org/wiki/File:Rebounder01.jpg
- Feature Toggle pic by Jason Zack Licenced under Creative Commons Attribution-Share Alike 2.5 Generic : https://commons.wikimedia.org/wiki/File:On-Off_Switch.jpg