Repository: sanctuary-js/sanctuary
Pull request #619 (2019-04-05 13:32:55)

array: specialize S.takeWhile and S.dropWhile
As explained in sanctuary-js/sanctuary-type-classes#122, these functions are currently unsound. `Z.takeWhile` and `Z.dropWhile` are to be removed in sanctuary-js/sanctuary-type-classes#123. `S.takeWhile` and `S.dropWhile` are useful functions, so it would be a shame to lose them entirely. Instead, let's specialize them.



Patch:
diff --git a/index.js b/index.js
index 90d86cf..4d9bcb7 100644
--- a/index.js
+++ b/index.js
@@ -1067,46 +1067,6 @@
     impl: curry2 (Z.reject)
   };
 
-  //# takeWhile :: Filterable f => (a -> Boolean) -> f a -> f a
-  //.
-  //. Curried version of [`Z.takeWhile`][]. Discards the first element which
-  //. does not satisfy the predicate, and all subsequent elements.
-  //.
-  //. See also [`dropWhile`](#dropWhile).
-  //.
-  //. ```javascript
-  //. > S.takeWhile (S.odd) ([3, 3, 3, 7, 6, 3, 5, 4])
-  //. [3, 3, 3, 7]
-  //.
-  //. > S.takeWhile (S.even) ([3, 3, 3, 7, 6, 3, 5, 4])
-  //. []
-  //. ```
-  _.takeWhile = {
-    consts: {f: [Z.Filterable]},
-    types: [$.Predicate (a), f (a), f (a)],
-    impl: curry2 (Z.takeWhile)
-  };
-
-  //# dropWhile :: Filterable f => (a -> Boolean) -> f a -> f a
-  //.
-  //. Curried version of [`Z.dropWhile`][]. Retains the first element which
-  //. does not satisfy the predicate, and all subsequent elements.
-  //.
-  //. See also [`takeWhile`](#takeWhile).
-  //.
-  //. ```javascript
-  //. > S.dropWhile (S.odd) ([3, 3, 3, 7, 6, 3, 5, 4])
-  //. [6, 3, 5, 4]
-  //.
-  //. > S.dropWhile (S.even) ([3, 3, 3, 7, 6, 3, 5, 4])
-  //. [3, 3, 3, 7, 6, 3, 5, 4]
-  //. ```
-  _.dropWhile = {
-    consts: {f: [Z.Filterable]},
-    types: [$.Predicate (a), f (a), f (a)],
-    impl: curry2 (Z.dropWhile)
-  };
-
   //# map :: Functor f => (a -> b) -> f a -> f b
   //.
   //. Curried version of [`Z.map`][].
@@ -3140,6 +3100,33 @@
     impl: takeLast
   };
 
+  //# takeWhile :: (a -> Boolean) -> Array a -> Array a
+  //.
+  //. Discards the first element which does not satisfy the predicate,
+  //. and all subsequent elements.
+  //.
+  //. See also [`dropWhile`](#dropWhile).
+  //.
+  //. ```javascript
+  //. > S.takeWhile (S.odd) ([3, 3, 3, 7, 6, 3, 5, 4])
+  //. [3, 3, 3, 7]
+  //.
+  //. > S.takeWhile (S.even) ([3, 3, 3, 7, 6, 3, 5, 4])
+  //. []
+  //. ```
+  function takeWhile(pred) {
+    return function(xs) {
+      var idx = 0;
+      while (idx < xs.length && pred (xs[idx])) idx += 1;
+      return xs.slice (0, idx);
+    };
+  }
+  _.takeWhile = {
+    consts: {},
+    types: [$.Predicate (a), $.Array (a), $.Array (a)],
+    impl: takeWhile
+  };
+
   //# drop :: Integer -> Array a -> Maybe (Array a)
   //.
   //. Returns Just all but the first N elements of the given array if N is
@@ -3195,6 +3182,33 @@
     impl: dropLast
   };
 
+  //# dropWhile :: (a -> Boolean) -> Array a -> Array a
+  //.
+  //. Retains the first element which does not satisfy the predicate,
+  //. and all subsequent elements.
+  //.
+  //. See also [`takeWhile`](#takeWhile).
+  //.
+  //. ```javascript
+  //. > S.dropWhile (S.odd) ([3, 3, 3, 7, 6, 3, 5, 4])
+  //. [6, 3, 5, 4]
+  //.
+  //. > S.dropWhile (S.even) ([3, 3, 3, 7, 6, 3, 5, 4])
+  //. [3, 3, 3, 7, 6, 3, 5, 4]
+  //. ```
+  function dropWhile(pred) {
+    return function(xs) {
+      var idx = 0;
+      while (idx < xs.length && pred (xs[idx])) idx += 1;
+      return xs.slice (idx);
+    };
+  }
+  _.dropWhile = {
+    consts: {},
+    types: [$.Predicate (a), $.Array (a), $.Array (a)],
+    impl: dropWhile
+  };
+
   //# size :: Foldable f => f a -> Integer
   //.
   //. Returns the number of elements of the given structure.
@@ -4903,7 +4917,6 @@
 //. [`Z.compose`]:              v:sanctuary-js/sanctuary-type-classes#compose
 //. [`Z.concat`]:               v:sanctuary-js/sanctuary-type-classes#concat
 //. [`Z.contramap`]:            v:sanctuary-js/sanctuary-type-classes#contramap
-//. [`Z.dropWhile`]:            v:sanctuary-js/sanctuary-type-classes#dropWhile
 //. [`Z.duplicate`]:            v:sanctuary-js/sanctuary-type-classes#duplicate
 //. [`Z.empty`]:                v:sanctuary-js/sanctuary-type-classes#empty
 //. [`Z.equals`]:               v:sanctuary-js/sanctuary-type-classes#equals
@@ -4925,7 +4938,6 @@
 //. [`Z.promap`]:               v:sanctuary-js/sanctuary-type-classes#promap
 //. [`Z.reject`]:               v:sanctuary-js/sanctuary-type-classes#reject
 //. [`Z.sequence`]:             v:sanctuary-js/sanctuary-type-classes#sequence
-//. [`Z.takeWhile`]:            v:sanctuary-js/sanctuary-type-classes#takeWhile
 //. [`Z.traverse`]:             v:sanctuary-js/sanctuary-type-classes#traverse
 //. [`Z.zero`]:                 v:sanctuary-js/sanctuary-type-classes#zero
 //. [`show`]:                   v:sanctuary-js/sanctuary-show#show


Test patch:
diff --git a/test/dropWhile.js b/test/dropWhile.js
index c3d9463..57beada 100644
--- a/test/dropWhile.js
+++ b/test/dropWhile.js
@@ -9,14 +9,10 @@ test ('dropWhile', () => {
 
   eq (typeof S.dropWhile) ('function');
   eq (S.dropWhile.length) (1);
-  eq (S.show (S.dropWhile)) ('dropWhile :: Filterable f => (a -> Boolean) -> f a -> f a');
+  eq (S.show (S.dropWhile)) ('dropWhile :: (a -> Boolean) -> Array a -> Array a');
 
   eq (S.dropWhile (S.odd) ([3, 3, 3, 7, 6, 3, 5, 4])) ([6, 3, 5, 4]);
   eq (S.dropWhile (S.even) ([3, 3, 3, 7, 6, 3, 5, 4])) ([3, 3, 3, 7, 6, 3, 5, 4]);
   eq (S.dropWhile (S.odd) ([])) ([]);
 
-  eq (S.dropWhile (S.odd) (S.Just (1))) (S.Nothing);
-  eq (S.dropWhile (S.even) (S.Just (1))) (S.Just (1));
-  eq (S.dropWhile (S.odd) (S.Nothing)) (S.Nothing);
-
 });
diff --git a/test/takeWhile.js b/test/takeWhile.js
index 7bc31e6..8d37ba7 100644
--- a/test/takeWhile.js
+++ b/test/takeWhile.js
@@ -9,14 +9,10 @@ test ('takeWhile', () => {
 
   eq (typeof S.takeWhile) ('function');
   eq (S.takeWhile.length) (1);
-  eq (S.show (S.takeWhile)) ('takeWhile :: Filterable f => (a -> Boolean) -> f a -> f a');
+  eq (S.show (S.takeWhile)) ('takeWhile :: (a -> Boolean) -> Array a -> Array a');
 
   eq (S.takeWhile (S.odd) ([3, 3, 3, 7, 6, 3, 5, 4])) ([3, 3, 3, 7]);
   eq (S.takeWhile (S.even) ([3, 3, 3, 7, 6, 3, 5, 4])) ([]);
   eq (S.takeWhile (S.odd) ([])) ([]);
 
-  eq (S.takeWhile (S.odd) (S.Just (1))) (S.Just (1));
-  eq (S.takeWhile (S.even) (S.Just (1))) (S.Nothing);
-  eq (S.takeWhile (S.odd) (S.Nothing)) (S.Nothing);
-
 });