A small set of combinators for defining extractors (à la Wadler/Scala/F#), patterns and rules. The only doc so far is that quick demo, but it demoes all the features of the library.
import 'package:patterns/patterns.dart';
import 'package:persistent/persistent.dart';
// We define linked lists.
class LList {}
class Nil implements LList {
toString() => "Nil()";
}
class Cons implements LList {
final x;
final LList xs;
Cons(this.x, this.xs);
toString() => "Cons($x, $xs)";
}
// This is boilerplate code that can be automatically derived from the
// definition of Nil and Cons. Note that patterns are typed:
// cons(eq(1), eq(2)) is ill-typed according to Dart's type system. You can
// also define more exotic patterns, like a pattern that matches any even number
// for instance.
OPattern<LList> nil() =>
constructor([],
(s) => (s is Nil) ? new Option.some([]) : new Option.none());
OPattern<LList> cons(OPattern p1, OPattern<LList> p2) =>
constructor([p1, p2],
(s) => (s is Cons) ? new Option.some([s.x, s.xs]) : new Option.none());
main() {
final list = new Cons(1, new Cons(2, new Cons(3, new Nil())));
// The right-hand side of the first rule that matches (in this case the
// last one) is executed. "pvar" denote pattern variables.
match(list).against(
nil() >> (_) { throw "should not happen"; }
| cons(v('x'), nil()) >> (_) { throw "should not happen"; }
| cons(v('x'), cons(eq(1), v('xs'))) >> (_) { throw "should not happen"; }
| cons(v('x'), cons(eq(2), v('xs'))) >> (e) { print("match: ${e['x']} ${e['xs']}"); }
); // prints "match: 1 Cons(3, Nil())"
// Match returns a value: the value returned by the executed right-hand side.
final tailOfTail = match(list).against(
cons(v('_'), cons(v('_'), v('xs'))) >> (e) { return e['xs']; } // _ is a wildcard
);
print(tailOfTail); // prints "Cons(3, Nil())"
// Non-linear patterns are supported.
final nonLinear = cons(v('x'), cons(v('x'), nil()));
match(new Cons(1, new Cons(2, new Nil()))).against(
nonLinear >> (_) { print("bad"); }
| v('_') >> (_) { print("good"); }
);
match(new Cons(1, new Cons(1, new Nil()))).against(
nonLinear >> (_) { print("good"); }
);
// If no branch matches, a MatchFailure is raised.
try {
match(list).against(
nil() >> (_) { throw "should not happen"; }
);
} on MatchFailure catch (_) {
print("failed as intended");
}
// Subpatterns can be aliased with %
match(list).against(
cons(v('_'), v('xs') % cons(v('_'), v('x'))) >> (e) { print("${e['xs']} ${e['x']}"); }
); // prints "Cons(2, Cons(3, Nil())) Cons(3, Nil())"
// Guards allow to put extra conditions on patterns.
match(list).against(
cons(v('x'), v('_')) & guard((e) => e['x'] > 1) >> (_) { throw "impossible"; }
& otherwise >> (e) { print("x = ${e['x']}"); }
| nil() >> (_) { throw "impossible"; }
); // prints "x = 1"
// The obligatory map function.
LList map(Function f, LList xs) =>
match(xs).against(
nil() >> (_) { return new Nil(); }
| cons(v('y'), v('ys')) >> (e) { return new Cons(f(e['y']), map(f, e['ys'])); }
);
print(map((n) => n + 1, list));
}
git clone https://github.com/polux/patterns
cd patterns
pub install
dart example/example.dart