Skip to content

Modular extensions for JDK 8, interop with Javaslang, FunctionalJava and GoogleGuava

License

Notifications You must be signed in to change notification settings

jartysiewicz/cyclops

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

cyclops

Powerful, modular extensions for Java 8. Take what you need or want.

cyclops - duke2

Cyclops Modules

Join the chat at https://gitter.im/aol/cyclops

Cyclops features

Cyclops Wiki

Cyclops articles across the web

Cyclops modules on Maven Central!

To import all modules use cyclops-all. For individual modules, see bottom of this page!

  • Cyclops All : Maven Central : cyclops-all

Import integration modules individually as needed.

  • Cyclops Javaslang : Maven Central : cyclops-javaslang
  • Cyclops Functional Java : Maven Central : cyclops-javaslang
  • Cyclops Guava : Maven Central : cyclops-guava

NB Cyclops All includes all Cyclops module except the integration modules (currently cyclops-javaslang).

cyclops module relationship - class diagram

Cyclops goals

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.

For Comprehensions

Perform nested operations on Collections or Monads.

for comprehensions

Example

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));

Pattern Matching

Advanced Scala-like pattern matching for Java 8. Match recursively against most Objects / datastructures.

whenvalues recursive

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

Example

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"));
	
}

Extensible Generic Monad Operations with AnyM and SequenceM

Example

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));

Example

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")));

Power Tuples

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

See Power Tuples wiki

Stackless Recursion with Trampoline

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));
 }

Try : functional exception handling

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

Example : Try with resources

Try.catchExceptions(FileNotFoundException.class,IOException.class)
			   .init(()->new BufferedReader(new FileReader("file.txt")))
			   .tryWithResources(this::read)
			   .onFail(this::recover)
			   .map(this::continueProcessing)

Production Enable / Disable Switch

  • 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).

Example

Switch<Feature> switch = createSwitch(config);

switch.map(this::processData); //if live, data is processed, otherwise nothing happens

Traits

  • 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

Function utilities

  • 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

cyclops-base

LazyImmutable

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

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);
 }

Attribution

Maven for individual modules

  • Cyclops Base : Maven Central : cyclops-base
  • Cyclops For Comprehensions : Maven Central : cyclops-for-comprehensions
  • Cyclops Pattern Matching : Maven Central : cyclops-for-comprehensions
  • Cyclops Functions : Maven Central : cyclops-for-comprehensions
  • Cyclops Core : Maven Central : cyclops-for-comprehensions
  • Cyclops Try : Maven Central : cyclops-for-comprehensions
  • Cyclops Trampoline : Maven Central : cyclops-for-comprehensions
  • Cyclops Enable Switch : Maven Central : cyclops-for-comprehensions
  • Cyclops Power Tuples : Maven Central : cyclops-for-comprehensions

About

Modular extensions for JDK 8, interop with Javaslang, FunctionalJava and GoogleGuava

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Java 99.0%
  • Groovy 1.0%