-
Notifications
You must be signed in to change notification settings - Fork 45
Pattern Matching Explained
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.
Pattern Matching is like a Java switch statement on steroids. Cyclops Pattern Matching, at a very low level is built on a simple combination of a Predicate (which determines the match) and Function (which determines the action on match). Cyclops then provides layers of builders on top of this to make complex matching statements succinct and simple.
Possibly the most common use case for pattern matching is to match on a particular value of an Object, or to match on it's type. When the argument matches the supplied type or value, we trigger the associated action for that case.
new MyCase(4,5,6).matchType(c ->c.isType((MyCase ce) -> "hello")
@Value
static class MyCase implements Matchable{
int a;
int b;
int c;
}

Matching.when().isValue(1).thenApply(v->"found 1")
.match(1).orElse("not found");

Another common use case is to test all or some of the values in a collection or the fields of a class. The Matchable interface provides two methods to do this match : which simply matches the values, and _match which also allows Type checking on the Collection / Class supplying the values.
new MyCase(1,2,3).match(this::cases);
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");
}
Wildcards such as Predicates.__ can also be used e.g.
private <I,T> CheckValues<Object, T> cases(CheckValues<I, T> c) {
return c.with(__,2,3).then(i->"hello")
.with(4,5,__).then(i->"goodbye");
}

It is possible to also pattern match against the fields or values in a Class / Collection recursively nested in the Object under test.
Predicates.with( ) can be used to further break down and match any Object.
e.g.
Matching.<Expression>whenValues().isType( (Add<Const,Mult> a)-> new Const(1))
.with(__,type(Mult.class).with(__,new Const(0)))
.whenValues().isType( (Add<Mult,Const> a)-> new Const(0)).with(type(Mult.class).with(__,new Const(0)),__)
.whenValues().isType( (Add<Add,Const> a)-> new Const(-100)).with(with(__,new Const(2)),__)
.apply(e).orElse(new Const(-1));
@AllArgsConstructor(access=AccessLevel.PRIVATE) static abstract class Expression implements Decomposable{}
final static class X extends Expression{ }
@Value final static class Const extends Expression { int value; }
@Value final static class Add<T extends Expression, R extends Expression> extends Expression { T left; R right; }
@Value final static class Mult<T extends Expression, R extends Expression> extends Expression { T left; R right; }
@Value final static class Neg<T extends Expression> extends Expression { T expr; }
As shown above Predicates.type can be used to Type check rather than value check a field - and as all predicates supplied are JDK predicates - it is possible to extend the framework with your own.
