Strong mode fixes (#61)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 40e62f1..eb76db2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,11 @@
+## 0.12.1+3
+
+* Make `predicate` and `pairwiseCompare` generic methods to allow typed
+ functions to be passed to them as arguments.
+
+* Make internal implementations take better advantage of type promotion to avoid
+  dynamic call overhead.
+
 ## 0.12.1+2
 
 * Fixed small documentation issues.
diff --git a/lib/src/core_matchers.dart b/lib/src/core_matchers.dart
index b513d5e..e8fdb11 100644
--- a/lib/src/core_matchers.dart
+++ b/lib/src/core_matchers.dart
@@ -110,6 +110,9 @@
     ? new _StringEqualsMatcher(expected)
     : new _DeepMatcher(expected, limit);
 
+typedef _RecursiveMatcher = List<String> Function(
+    dynamic, dynamic, String, int);
+
 class _DeepMatcher extends Matcher {
   final _expected;
   final int _limit;
@@ -117,52 +120,60 @@
   _DeepMatcher(this._expected, [int limit = 1000]) : this._limit = limit;
 
   // Returns a pair (reason, location)
-  List _compareIterables(expected, actual, matcher, depth, location) {
-    if (actual is! Iterable) return ['is not Iterable', location];
+  List<String> _compareIterables(Iterable expected, Object actual,
+      _RecursiveMatcher matcher, int depth, String location) {
+    if (actual is Iterable) {
+      var expectedIterator = expected.iterator;
+      var actualIterator = actual.iterator;
+      for (var index = 0;; index++) {
+        // Advance in lockstep.
+        var expectedNext = expectedIterator.moveNext();
+        var actualNext = actualIterator.moveNext();
 
-    var expectedIterator = expected.iterator;
-    var actualIterator = actual.iterator;
-    for (var index = 0;; index++) {
-      // Advance in lockstep.
-      var expectedNext = expectedIterator.moveNext();
-      var actualNext = actualIterator.moveNext();
+        // If we reached the end of both, we succeeded.
+        if (!expectedNext && !actualNext) return null;
 
-      // If we reached the end of both, we succeeded.
-      if (!expectedNext && !actualNext) return null;
+        // Fail if their lengths are different.
+        var newLocation = '$location[$index]';
+        if (!expectedNext) return ['longer than expected', newLocation];
+        if (!actualNext) return ['shorter than expected', newLocation];
 
-      // Fail if their lengths are different.
-      var newLocation = '$location[$index]';
-      if (!expectedNext) return ['longer than expected', newLocation];
-      if (!actualNext) return ['shorter than expected', newLocation];
-
-      // Match the elements.
-      var rp = matcher(
-          expectedIterator.current, actualIterator.current, newLocation, depth);
-      if (rp != null) return rp;
-    }
-  }
-
-  List _compareSets(Set expected, actual, matcher, depth, location) {
-    if (actual is! Iterable) return ['is not Iterable', location];
-    actual = actual.toSet();
-
-    for (var expectedElement in expected) {
-      if (actual.every((actualElement) =>
-          matcher(expectedElement, actualElement, location, depth) != null)) {
-        return ['does not contain $expectedElement', location];
+        // Match the elements.
+        var rp = matcher(expectedIterator.current, actualIterator.current,
+            newLocation, depth);
+        if (rp != null) return rp;
       }
-    }
-
-    if (actual.length > expected.length) {
-      return ['larger than expected', location];
-    } else if (actual.length < expected.length) {
-      return ['smaller than expected', location];
     } else {
-      return null;
+      return ['is not Iterable', location];
     }
   }
 
-  List _recursiveMatch(expected, actual, String location, int depth) {
+  List<String> _compareSets(Set expected, Object actual,
+      _RecursiveMatcher matcher, int depth, String location) {
+    if (actual is Iterable) {
+      Set other = actual.toSet();
+
+      for (var expectedElement in expected) {
+        if (other.every((actualElement) =>
+            matcher(expectedElement, actualElement, location, depth) != null)) {
+          return ['does not contain $expectedElement', location];
+        }
+      }
+
+      if (other.length > expected.length) {
+        return ['larger than expected', location];
+      } else if (other.length < expected.length) {
+        return ['smaller than expected', location];
+      } else {
+        return null;
+      }
+    } else {
+      return ['is not Iterable', location];
+    }
+  }
+
+  List<String> _recursiveMatch(
+      Object expected, Object actual, String location, int depth) {
     // If the expected value is a matcher, try to match it.
     if (expected is Matcher) {
       var matchState = {};
@@ -193,17 +204,16 @@
             expected, actual, _recursiveMatch, depth + 1, location);
       } else if (expected is Map) {
         if (actual is! Map) return ['expected a map', location];
-
-        var err = (expected.length == actual.length)
-            ? ''
-            : 'has different length and ';
+        var map = (actual as Map);
+        var err =
+            (expected.length == map.length) ? '' : 'has different length and ';
         for (var key in expected.keys) {
-          if (!actual.containsKey(key)) {
+          if (!map.containsKey(key)) {
             return ["${err}is missing map key '$key'", location];
           }
         }
 
-        for (var key in actual.keys) {
+        for (var key in map.keys) {
           if (!expected.containsKey(key)) {
             return ["${err}has extra map key '$key'", location];
           }
@@ -211,7 +221,7 @@
 
         for (var key in expected.keys) {
           var rp = _recursiveMatch(
-              expected[key], actual[key], "$location['$key']", depth + 1);
+              expected[key], map[key], "$location['$key']", depth + 1);
           if (rp != null) return rp;
         }
 
@@ -239,7 +249,7 @@
   String _match(expected, actual, Map matchState) {
     var rp = _recursiveMatch(expected, actual, '', 0);
     if (rp == null) return null;
-    var reason;
+    String reason;
     if (rp[0].length > 0) {
       if (rp[1].length > 0) {
         reason = "${rp[0]} at location ${rp[1]}";
@@ -567,18 +577,19 @@
 /// For example:
 ///
 ///     expect(v, predicate((x) => ((x % 2) == 0), "is even"))
-Matcher predicate(bool f(value), [String description = 'satisfies function']) =>
+Matcher predicate<T>(bool f(T value),
+        [String description = 'satisfies function']) =>
     new _Predicate(f, description);
 
-typedef bool _PredicateFunction(value);
+typedef bool _PredicateFunction<T>(T value);
 
-class _Predicate extends Matcher {
-  final _PredicateFunction _matcher;
+class _Predicate<T> extends Matcher {
+  final _PredicateFunction<T> _matcher;
   final String _description;
 
-  const _Predicate(this._matcher, this._description);
+  _Predicate(this._matcher, this._description);
 
-  bool matches(item, Map matchState) => _matcher(item);
+  bool matches(item, Map matchState) => _matcher(item as T);
 
   Description describe(Description description) =>
       description.add(_description);
diff --git a/lib/src/iterable_matchers.dart b/lib/src/iterable_matchers.dart
index 4f7785c..759c252 100644
--- a/lib/src/iterable_matchers.dart
+++ b/lib/src/iterable_matchers.dart
@@ -155,42 +155,45 @@
       : _expected = expected.map(wrapMatcher).toList();
 
   String _test(item) {
-    if (item is! Iterable) return 'not iterable';
-    item = item.toList();
+    if (item is Iterable) {
+      var list = item.toList();
 
-    // Check the lengths are the same.
-    if (_expected.length > item.length) {
-      return 'has too few elements (${item.length} < ${_expected.length})';
-    } else if (_expected.length < item.length) {
-      return 'has too many elements (${item.length} > ${_expected.length})';
-    }
+      // Check the lengths are the same.
+      if (_expected.length > list.length) {
+        return 'has too few elements (${list.length} < ${_expected.length})';
+      } else if (_expected.length < list.length) {
+        return 'has too many elements (${list.length} > ${_expected.length})';
+      }
 
-    var matched = new List<bool>.filled(item.length, false);
-    var expectedPosition = 0;
-    for (var expectedMatcher in _expected) {
-      var actualPosition = 0;
-      var gotMatch = false;
-      for (var actualElement in item) {
-        if (!matched[actualPosition]) {
-          if (expectedMatcher.matches(actualElement, {})) {
-            matched[actualPosition] = gotMatch = true;
-            break;
+      var matched = new List<bool>.filled(list.length, false);
+      var expectedPosition = 0;
+      for (var expectedMatcher in _expected) {
+        var actualPosition = 0;
+        var gotMatch = false;
+        for (var actualElement in list) {
+          if (!matched[actualPosition]) {
+            if (expectedMatcher.matches(actualElement, {})) {
+              matched[actualPosition] = gotMatch = true;
+              break;
+            }
           }
+          ++actualPosition;
         }
-        ++actualPosition;
-      }
 
-      if (!gotMatch) {
-        return new StringDescription()
-            .add('has no match for ')
-            .addDescriptionOf(expectedMatcher)
-            .add(' at index $expectedPosition')
-            .toString();
-      }
+        if (!gotMatch) {
+          return new StringDescription()
+              .add('has no match for ')
+              .addDescriptionOf(expectedMatcher)
+              .add(' at index $expectedPosition')
+              .toString();
+        }
 
-      ++expectedPosition;
+        ++expectedPosition;
+      }
+      return null;
+    } else {
+      return 'not iterable';
     }
-    return null;
   }
 
   bool matches(item, Map mismatchState) => _test(item) == null;
@@ -210,34 +213,37 @@
 /// The [comparator] function, taking an expected and an actual argument, and
 /// returning whether they match, will be applied to each pair in order.
 /// [description] should be a meaningful name for the comparator.
-Matcher pairwiseCompare(
-        Iterable expected, bool comparator(a, b), String description) =>
+Matcher pairwiseCompare<S, T>(
+        Iterable<S> expected, bool comparator(S a, T b), String description) =>
     new _PairwiseCompare(expected, comparator, description);
 
-typedef bool _Comparator(a, b);
+typedef bool _Comparator<S, T>(S a, T b);
 
-class _PairwiseCompare extends _IterableMatcher {
-  final Iterable _expected;
-  final _Comparator _comparator;
+class _PairwiseCompare<S, T> extends _IterableMatcher {
+  final Iterable<S> _expected;
+  final _Comparator<S, T> _comparator;
   final String _description;
 
   _PairwiseCompare(this._expected, this._comparator, this._description);
 
   bool matches(item, Map matchState) {
-    if (item is! Iterable) return false;
-    if (item.length != _expected.length) return false;
-    var iterator = item.iterator;
-    var i = 0;
-    for (var e in _expected) {
-      iterator.moveNext();
-      if (!_comparator(e, iterator.current)) {
-        addStateInfo(matchState,
-            {'index': i, 'expected': e, 'actual': iterator.current});
-        return false;
+    if (item is Iterable) {
+      if (item.length != _expected.length) return false;
+      var iterator = item.iterator;
+      var i = 0;
+      for (var e in _expected) {
+        iterator.moveNext();
+        if (!_comparator(e, iterator.current)) {
+          addStateInfo(matchState,
+              {'index': i, 'expected': e, 'actual': iterator.current});
+          return false;
+        }
+        i++;
       }
-      i++;
+      return true;
+    } else {
+      return false;
     }
-    return true;
   }
 
   Description describe(Description description) =>
diff --git a/lib/src/numeric_matchers.dart b/lib/src/numeric_matchers.dart
index e8651de..c405391 100644
--- a/lib/src/numeric_matchers.dart
+++ b/lib/src/numeric_matchers.dart
@@ -17,11 +17,13 @@
   const _IsCloseTo(this._value, this._delta);
 
   bool matches(item, Map matchState) {
-    if (item is! num) return false;
-
-    var diff = item - _value;
-    if (diff < 0) diff = -diff;
-    return (diff <= _delta);
+    if (item is num) {
+      var diff = item - _value;
+      if (diff < 0) diff = -diff;
+      return (diff <= _delta);
+    } else {
+      return false;
+    }
   }
 
   Description describe(Description description) => description
@@ -32,12 +34,12 @@
 
   Description describeMismatch(
       item, Description mismatchDescription, Map matchState, bool verbose) {
-    if (item is! num) {
-      return mismatchDescription.add(' not numeric');
-    } else {
+    if (item is num) {
       var diff = item - _value;
       if (diff < 0) diff = -diff;
       return mismatchDescription.add(' differs by ').addDescriptionOf(diff);
+    } else {
+      return mismatchDescription.add(' not numeric');
     }
   }
 }
@@ -70,19 +72,20 @@
       this._low, this._high, this._lowMatchValue, this._highMatchValue);
 
   bool matches(value, Map matchState) {
-    if (value is! num) {
+    if (value is num) {
+      if (value < _low || value > _high) {
+        return false;
+      }
+      if (value == _low) {
+        return _lowMatchValue;
+      }
+      if (value == _high) {
+        return _highMatchValue;
+      }
+      return true;
+    } else {
       return false;
     }
-    if (value < _low || value > _high) {
-      return false;
-    }
-    if (value == _low) {
-      return _lowMatchValue;
-    }
-    if (value == _high) {
-      return _highMatchValue;
-    }
-    return true;
   }
 
   Description describe(Description description) =>
diff --git a/lib/src/string_matchers.dart b/lib/src/string_matchers.dart
index d8bbdb8..14e6fbb 100644
--- a/lib/src/string_matchers.dart
+++ b/lib/src/string_matchers.dart
@@ -118,15 +118,16 @@
   const _StringContainsInOrder(this._substrings);
 
   bool matches(item, Map matchState) {
-    if (!(item is String)) {
+    if (item is String) {
+      var from_index = 0;
+      for (var s in _substrings) {
+        from_index = item.indexOf(s, from_index);
+        if (from_index < 0) return false;
+      }
+      return true;
+    } else {
       return false;
     }
-    var from_index = 0;
-    for (var s in _substrings) {
-      from_index = item.indexOf(s, from_index);
-      if (from_index < 0) return false;
-    }
-    return true;
   }
 
   Description describe(Description description) => description.addAll(
diff --git a/pubspec.yaml b/pubspec.yaml
index ae72221..1d130cc 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,10 +1,10 @@
 name: matcher
-version: 0.12.1+2
+version: 0.12.1+3
 author: Dart Team <misc@dartlang.org>
 description: Support for specifying test expectations
 homepage: https://github.com/dart-lang/matcher
 environment:
-  sdk: '>=1.8.0 <2.0.0-dev.infinity'
+  sdk: '>=1.23.0 <2.0.0-dev.infinity'
 dependencies:
   stack_trace: '^1.2.0'
 dev_dependencies: