Repository: sanctuary-js/sanctuary
Pull request #448 (2017-10-12 17:24:52)

logic: generalize S.allPass and S.anyPass
@gabejohnson suggested this improvement on Gitter a few minutes ago. :)



Patch:
diff --git a/index.js b/index.js
index 21ba3eb..aeb5023 100644
--- a/index.js
+++ b/index.js
@@ -3001,12 +3001,12 @@
   }
   S.unless = def('unless', {}, [$.Predicate(a), Fn(a, a), a, a], unless);
 
-  //# allPass :: Array (a -> Boolean) -> a -> Boolean
+  //# allPass :: Foldable f => f (a -> Boolean) -> a -> Boolean
   //.
-  //. Takes an array of unary predicates and a value of any type
-  //. and returns `true` if all the predicates pass; `false` otherwise.
-  //. None of the subsequent predicates will be evaluated after the
-  //. first failed predicate.
+  //. Takes a structure containing zero or more predicates, and a value of
+  //. any type. Returns `true` if the value satisfies all of the predicates;
+  //. `false` otherwise. None of the subsequent predicates will be applied
+  //. after the first predicate not satisfied.
   //.
   //. ```javascript
   //. > S.allPass([S.test(/q/), S.test(/u/), S.test(/i/)], 'quiessence')
@@ -3016,17 +3016,20 @@
   //. false
   //. ```
   function allPass(preds, x) {
-    return preds.every(function(p) { return p(x); });
+    return Z.reduce(function(b, p) { return b && p(x); }, true, preds);
   }
   S.allPass =
-  def('allPass', {}, [$.Array($.Predicate(a)), a, $.Boolean], allPass);
+  def('allPass',
+      {f: [Z.Foldable]},
+      [f($.Predicate(a)), a, $.Boolean],
+      allPass);
 
-  //# anyPass :: Array (a -> Boolean) -> a -> Boolean
+  //# anyPass :: Foldable f => f (a -> Boolean) -> a -> Boolean
   //.
-  //. Takes an array of unary predicates and a value of any type
-  //. and returns `true` if any of the predicates pass; `false` otherwise.
-  //. None of the subsequent predicates will be evaluated after the
-  //. first passed predicate.
+  //. Takes a structure containing zero or more predicates, and a value of
+  //. any type. Returns `true` if the value satisfies any of the predicates;
+  //. `false` otherwise. None of the subsequent predicates will be applied
+  //. after the first predicate satisfied.
   //.
   //. ```javascript
   //. > S.anyPass([S.test(/q/), S.test(/u/), S.test(/i/)], 'incandescent')
@@ -3036,10 +3039,13 @@
   //. false
   //. ```
   function anyPass(preds, x) {
-    return preds.some(function(p) { return p(x); });
+    return Z.reduce(function(b, p) { return b || p(x); }, false, preds);
   }
   S.anyPass =
-  def('anyPass', {}, [$.Array($.Predicate(a)), a, $.Boolean], anyPass);
+  def('anyPass',
+      {f: [Z.Foldable]},
+      [f($.Predicate(a)), a, $.Boolean],
+      anyPass);
 
   //. ### List
   //.


Test patch:
diff --git a/test/allPass.js b/test/allPass.js
index 70774cc..998421a 100644
--- a/test/allPass.js
+++ b/test/allPass.js
@@ -1,20 +1,29 @@
 'use strict';
 
-var S = require('..');
+var S = require('./internal/sanctuary');
 
+var List = require('./internal/List');
 var eq = require('./internal/eq');
 
 
+var Cons = List.Cons;
+var Nil = List.Nil;
+
+
 test('allPass', function() {
 
   eq(typeof S.allPass, 'function');
   eq(S.allPass.length, 2);
-  eq(S.allPass.toString(), 'allPass :: Array (a -> Boolean) -> a -> Boolean');
+  eq(S.allPass.toString(), 'allPass :: Foldable f => f (a -> Boolean) -> a -> Boolean');
 
   eq(S.allPass([], 'abacus'), true);
   eq(S.allPass([S.test(/a/), S.test(/b/), S.test(/c/)], 'abacus'), true);
   eq(S.allPass([S.test(/a/), S.test(/b/), S.test(/c/)], 'banana'), false);
 
+  eq(S.allPass(Nil, 'abacus'), true);
+  eq(S.allPass(Cons(S.test(/a/), Cons(S.test(/b/), Cons(S.test(/c/), Nil))), 'abacus'), true);
+  eq(S.allPass(Cons(S.test(/a/), Cons(S.test(/b/), Cons(S.test(/c/), Nil))), 'banana'), false);
+
   var e = false;
   eq(S.allPass([S.test(/a/), function() { e = true; }], 'monkey'), false);
   eq(e, false);
diff --git a/test/anyPass.js b/test/anyPass.js
index d910608..331ffe0 100644
--- a/test/anyPass.js
+++ b/test/anyPass.js
@@ -1,19 +1,28 @@
 'use strict';
 
-var S = require('..');
+var S = require('./internal/sanctuary');
 
+var List = require('./internal/List');
 var eq = require('./internal/eq');
 
 
+var Cons = List.Cons;
+var Nil = List.Nil;
+
+
 test('anyPass', function() {
 
   eq(typeof S.anyPass, 'function');
   eq(S.anyPass.length, 2);
-  eq(S.anyPass.toString(), 'anyPass :: Array (a -> Boolean) -> a -> Boolean');
+  eq(S.anyPass.toString(), 'anyPass :: Foldable f => f (a -> Boolean) -> a -> Boolean');
 
-  eq(S.anyPass([], 'narwhal'), false);
+  eq(S.anyPass([], 'dolphin'), false);
+  eq(S.anyPass([S.test(/a/), S.test(/b/), S.test(/c/)], 'dolphin'), false);
   eq(S.anyPass([S.test(/a/), S.test(/b/), S.test(/c/)], 'narwhal'), true);
-  eq(S.anyPass([S.test(/a/), S.test(/b/), S.test(/c/)], 'gorilla'), true);
+
+  eq(S.anyPass(Nil, 'dolphin'), false);
+  eq(S.anyPass(Cons(S.test(/a/), Cons(S.test(/b/), Cons(S.test(/c/), Nil))), 'dolphin'), false);
+  eq(S.anyPass(Cons(S.test(/a/), Cons(S.test(/b/), Cons(S.test(/c/), Nil))), 'narwhal'), true);
 
   var e = false;
   eq(S.anyPass([S.test(/a/), function() { e = true; }], 'narwhal'), true);