match 0.4.1 copy "match: ^0.4.1" to clipboard
match: ^0.4.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.4.1
dev_dependencies:
  build_runner:
  match_generator: ^0.4.1
copied to clipboard

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({required this.value});
}

class Add implements Expr {
  Expr e1;
  Expr e2;
  Add({required this.e1, required this.e2});
}
copied to clipboard

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);
copied to clipboard

The match method takes a required named function argument per subclass. matchAny is like match but with optional named functions, instead it takes a required named function argument any that will called if none of the provided arguments matches.

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);
copied to clipboard

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);
copied to clipboard

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 either e1 or e2 matches x.
  • e1 & e2 the "and" operator matches x if both e1 and e2 matches x.

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);
copied to clipboard
  • e1 > bool where the right hand side of > is a boolean expression that needs to return true for x to match the case.
  • e1 >> bool Function() where the right hand side of >> is a boolean function that needs to return true for the x 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);
copied to clipboard

Supported match functions:

  • eq(s) matches if x == s
  • any matches any values of x

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);
copied to clipboard

Supported match functions:

  • eq(n) matches if x == n
  • lt(n) matches if x < n
  • gt(n) matches if x > n
  • lte(n) matches if x <= n
  • gte(n) matches if x >= n
  • range(from, to) matches if x >= from && x <= to
  • any matches any values of x
23
likes
150
points
165
downloads

Publisher

verified publisherbech.io

Weekly Downloads

2024.08.16 - 2025.02.28

Dart library with match annotation for generating custom match extensions and extension methods for dart builtin types

Repository (GitHub)

Documentation

API reference

License

MIT (license)

Dependencies

collection

More

Packages that depend on match