Tighten type for argument to `evaluate` (#22)

Taking a `dynamic` throws away the static help that a caller would get
with an argument type. Since it is easy to refactor callers currently
passing an `Iterable<String>` to tear off the `.contains` method we can
take the Function type always. This also improves inference for argument
types in places that are passing a function literal.
diff --git a/.travis.yml b/.travis.yml
index 8590a67..d2a0ee3 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,7 +1,7 @@
 language: dart
 
 dart:
-  - 2.0.0
+  - 2.4.0
   - dev
 
 dart_task:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d6cdc28..7e2aef0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,10 @@
+## 2.0.0
+
+* Breaking: `BooleanSelector.evaluate` always takes a `bool Function(String)`.
+  For use cases previously passing a `Set<String>`, tear off the `.contains`
+  method. For use cases passing an `Iterable<String>` it may be worthwhile to
+  first use `.toSet()` before tearing off `.contains`.
+
 ## 1.0.5
 
 * Update package metadata & add `example/` folder
diff --git a/README.md b/README.md
index 33c2eb5..8f636a6 100644
--- a/README.md
+++ b/README.md
@@ -17,9 +17,8 @@
 
 A boolean selector is parsed from a string using
 [`new BooleanSelector.parse()`][parse], and evaluated against a set of variables
-using [`BooleanSelector.evaluate()`][evaluate]. The variables may be supplied as
-a list of strings, or as a function that takes a variable name and returns its
-value. For example:
+using [`BooleanSelector.evaluate()`][evaluate]. The variables are supplied as
+a function that takes a variable name and returns its value. For example:
 
 [parse]: https://pub.dev/documentation/boolean_selector/latest/boolean_selector/BooleanSelector/BooleanSelector.parse.html
 
diff --git a/lib/boolean_selector.dart b/lib/boolean_selector.dart
index e32eb40..3a66ec4 100644
--- a/lib/boolean_selector.dart
+++ b/lib/boolean_selector.dart
@@ -36,11 +36,9 @@
 
   /// Returns whether the selector matches the given [semantics].
   ///
-  /// The [semantics] define which variables evaluate to `true` or `false`. The
-  /// parameter can be either an `Iterable<String>` containing variables that
-  /// should evaluate to `true`, or a function `bool semantics(String variable)`
-  /// that returns a variable's value.
-  bool evaluate(semantics);
+  /// The [semantics] define which variables evaluate to `true` or `false`. When
+  /// passed a variable name it should return the value of that variable.
+  bool evaluate(bool Function(String variable) semantics);
 
   /// Returns a new [BooleanSelector] that matches only inputs matched by both
   /// [this] and [other].
diff --git a/lib/src/all.dart b/lib/src/all.dart
index d634b9f..9255709 100644
--- a/lib/src/all.dart
+++ b/lib/src/all.dart
@@ -14,7 +14,7 @@
   const All();
 
   @override
-  bool evaluate(semantics) => true;
+  bool evaluate(bool Function(String variable) semantics) => true;
 
   @override
   BooleanSelector intersection(BooleanSelector other) => other;
diff --git a/lib/src/evaluator.dart b/lib/src/evaluator.dart
index c15c0ca..ee57149 100644
--- a/lib/src/evaluator.dart
+++ b/lib/src/evaluator.dart
@@ -5,18 +5,12 @@
 import 'ast.dart';
 import 'visitor.dart';
 
-typedef _Semantics = bool Function(String variable);
-
 /// A visitor for evaluating boolean selectors against a specific set of
 /// semantics.
 class Evaluator implements Visitor<bool> {
-  /// The semantics to evaluate against.
-  final _Semantics _semantics;
+  final bool Function(String variable) _semantics;
 
-  Evaluator(semantics)
-      : _semantics = semantics is Iterable
-            ? semantics.toSet().contains
-            : semantics as _Semantics;
+  Evaluator(this._semantics);
 
   @override
   bool visitVariable(VariableNode node) => _semantics(node.name);
diff --git a/lib/src/impl.dart b/lib/src/impl.dart
index 3cd87c8..16dc3ec 100644
--- a/lib/src/impl.dart
+++ b/lib/src/impl.dart
@@ -32,7 +32,8 @@
   Iterable<String> get variables => _selector.variables;
 
   @override
-  bool evaluate(semantics) => _selector.accept(Evaluator(semantics));
+  bool evaluate(bool Function(String variable) semantics) =>
+      _selector.accept(Evaluator(semantics));
 
   @override
   BooleanSelector intersection(BooleanSelector other) {
diff --git a/lib/src/none.dart b/lib/src/none.dart
index 1fc616f..42340b1 100644
--- a/lib/src/none.dart
+++ b/lib/src/none.dart
@@ -14,7 +14,7 @@
   const None();
 
   @override
-  bool evaluate(semantics) => false;
+  bool evaluate(bool Function(String variable) semantics) => false;
 
   @override
   BooleanSelector intersection(BooleanSelector other) => this;
diff --git a/lib/src/union_selector.dart b/lib/src/union_selector.dart
index c985be9..838e3d4 100644
--- a/lib/src/union_selector.dart
+++ b/lib/src/union_selector.dart
@@ -17,7 +17,7 @@
       _selector1.variables.toList()..addAll(_selector2.variables);
 
   @override
-  bool evaluate(semantics) =>
+  bool evaluate(bool Function(String variable) semantics) =>
       _selector1.evaluate(semantics) || _selector2.evaluate(semantics);
 
   @override
diff --git a/pubspec.yaml b/pubspec.yaml
index ba9a4b8..73cd68b 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,12 +1,12 @@
 name: boolean_selector
-version: 1.0.6-dev
+version: 2.0.0
 description: >-
   A flexible syntax for boolean expressions, based on a simplified version of
   Dart's expression syntax.
 homepage: https://github.com/dart-lang/boolean_selector
 
 environment:
-  sdk: '>=2.0.0 <3.0.0'
+  sdk: '>=2.4.0 <3.0.0'
 
 dependencies:
   source_span: ^1.0.0
@@ -15,3 +15,8 @@
 dev_dependencies:
   pedantic: ^1.0.0
   test: ^1.2.0
+
+dependency_overrides:
+  test: 1.11.1
+  test_api: 0.2.13
+  test_core: 0.2.18
diff --git a/test/evaluate_test.dart b/test/evaluate_test.dart
index 9df5fdc..5dfc2cc 100644
--- a/test/evaluate_test.dart
+++ b/test/evaluate_test.dart
@@ -36,19 +36,17 @@
   });
 
   test('with a semantics function', () {
-    _expectEval('foo', false,
-        semantics: (String variable) => variable.contains('a'));
-    _expectEval('bar', true,
-        semantics: (String variable) => variable.contains('a'));
-    _expectEval('baz', true,
-        semantics: (String variable) => variable.contains('a'));
+    _expectEval('foo', false, semantics: (variable) => variable.contains('a'));
+    _expectEval('bar', true, semantics: (variable) => variable.contains('a'));
+    _expectEval('baz', true, semantics: (variable) => variable.contains('a'));
   });
 }
 
 /// Asserts that [expression] evaluates to [result] against [semantics].
 ///
 /// By default, "true" is true and all other variables are "false".
-void _expectEval(String expression, bool result, {semantics}) {
+void _expectEval(String expression, bool result,
+    {bool Function(String variable) semantics}) {
   expect(_eval(expression, semantics: semantics), equals(result),
       reason: 'Expected "$expression" to evaluate to $result.');
 }
@@ -56,7 +54,7 @@
 /// Returns the result of evaluating [expression] on [semantics].
 ///
 /// By default, "true" is true and all other variables are "false".
-bool _eval(String expression, {semantics}) {
+bool _eval(String expression, {bool Function(String variable) semantics}) {
   var selector = BooleanSelector.parse(expression);
-  return selector.evaluate(semantics ?? ['true']);
+  return selector.evaluate(semantics ?? (v) => v == 'true');
 }