match 0.3.1 match: ^0.3.1 copied to clipboard
Dart library with match annotation for generating custom match extensions and extension methods for dart builtin types
match #
match
extension methods for Dart. Inspired by pattern matching from functional
programming languages and Kotlin's when
expression.
This library contains @match
annotation for generating custom match
and
matchAny
extensions and extension methods for dart builtin types.
Setup #
Add the following to your pubspec.yaml:
dependencies:
match: ^0.2.0
dev_dependencies:
build_runner:
match_generator: ^0.2.0
If you are using the @match
annotation run:
pub run build_runner build
Class match #
Similar to sealed
classes in Kotlin (discriminated unions) a match
and
matchAny
extension method for a single level class hierarchy can be generated
using the @match
annotation. All classes must be defined in the same file.
//types.dart:
import 'package:match/match.dart';
part 'types.g.dart';
@match
abstract class Expr {}
class Value implements Expr {
int value;
Value({this.value});
}
class Add implements Expr {
Expr e1;
Expr e2;
Add({this.e1, this.e2});
}
match
and matchAny
extension methods on Expr
function will be generated in
the types.g.dart
file. And can be used as follows:
int eval(Expr expr) {
return expr.match(
value: (v) => v.value,
add: (a) => eval(a.e1) + eval(a.e2),
);
}
final e = Add(
e1: Add(e1: Value(value: 10), e2: Value(value: 20)),
e2: Value(value: 20),
);
expect(eval(e), 50);
final v = Value(value: 20);
final result = v.matchAny(add: (a) => 1, any: () => 2);
expect(result, 2);
The match
method takes an named function argument per subclass. matchAny
is
like match
but also takes a named function argument any
that will called if
none of the provided arguments matches. If any
is not provided and no argument
matches, an exception will be thrown at runtime.
The match
and matchAny
method's named arguments are annotated with @required
to
allow dartanalyzer
to give warnings when cases are missing.
Enum match #
An enum
can also be annotated with @match
to generate a match
and
matchAny
extension methods:
//types.dart:
import 'package:match/match.dart';
part 'types.g.dart';
@match
enum Color {
red,
green,
blue,
}
final r = Color.red;
final result = r.match(
red: () => 1,
green: () => 2,
blue: () => 3,
);
expect(result, 1);
final result = r.matchAny(
green: () => 1,
any: () => 2,
);
expect(result, 2);
Dart builtin types match #
For Dart builtin types the match
extension methods works a bit differently.
Here we have a DSL for match cases that allows for advanced value matching. Lets
look at an example where we match an integer:
import 'package:match/match.dart';
final x = 10;
final y = true;
final result = x.match({
gt(100): () => 1,
eq(10) | eq(20): () => 2,
range(30, 40): () => 3,
any: () => 3,
});
expect(result, 2);
Each case is expressed using a tiny DSL that allows for building more complex matching. The
DSL consists of functions that matches the value of x
and operators that
combines the functions for more complicated matching. If a case matches the
values of x
the corresponding function is run and the result returned. If no
cases matches, an exception will be thrown at runtime.
We have the following general combining operators for the DSL:
e1 | e2
the "or" operator matches x if eithere1
ore2
matchesx
.e1 & e2
the "and" operator matches x if bothe1
ande2
matchesx
.
Additionally we have the "guard" operators >
and >>
:
import 'package:match/match.dart';
final x = 10;
final y = true;
final result = x.match({
eq(10) > !y : () => 1,
eq(10) > y : () => 2,
eq(10) >> () => !y : () => 3,
eq(10) >> () => y : () => 4,
});
expect(result, 2);
e1 > bool
where the right hand side of>
is a boolean expression that needs to return true forx
to match the case.e1 >> bool Function()
where the right hand side of>>
is a boolean function that needs to return true for thex
to match the case.
String.match #
import 'package:match/match.dart';
final s = 'aaa';
final result = s.match({
eq('aaa') | eq('ccc'): () => 1,
eq('bbb'): () => 2,
any: () => 3,
});
expect(result, 1);
Supported match functions:
eq(s)
matches ifx == s
any
matches any values ofx
num.match (double/int) #
import 'package:match/match.dart';
final x = 10;
final result = x.match({
gt(100): () => 1,
eq(10) | eq(20): () => 2,
range(30, 40): () => 3,
any: () => 3,
});
expect(result, 2);
Supported match functions:
eq(n)
matches ifx == n
lt(n)
matches ifx < n
gt(n)
matches ifx > n
lte(n)
matches ifx <= n
gte(n)
matches ifx >= n
range(from, to)
matches ifx >= from && x <= to
any
matches any values ofx