Repository: sanctuary-js/sanctuary
Pull request #516 (2018-04-01 22:22:22)

array: remove unprincipled List type constructor
The time has come. :)

The following functions will no longer operate on strings:

  - `S.at`
  - `S.drop`
  - `S.dropLast`
  - `S.head`
  - `S.init`
  - `S.last`
  - `S.slice`
  - `S.tail`
  - `S.take`
  - `S.takeLast`



Patch:
diff --git a/index.js b/index.js
index 57c1d81..7a6fa96 100644
--- a/index.js
+++ b/index.js
@@ -305,14 +305,6 @@
     function(either) { return either.isRight ? [either.value] : []; }
   );
 
-  //  List :: Type -> Type
-  var List = $.UnaryType(
-    'sanctuary/List',
-    readmeUrl('list'),
-    function(x) { return $.String._test(x) || Array.isArray(x); },
-    function(list) { return $.String._test(list) ? [] : list; }
-  );
-
   //  maybeTypeIdent :: String
   var maybeTypeIdent = 'sanctuary/Maybe';
 
@@ -3265,29 +3257,13 @@
     impl: anyPass
   };
 
-  //. ### List
-  //.
-  //. The List type constructor enables type signatures to describe ad hoc
-  //. polymorphic functions which operate on either [`Array`][$.Array] or
-  //. [`String`][$.String] values.
-  //.
-  //. Mental gymnastics are required to treat arrays and strings similarly.
-  //. `[1, 2, 3]` is a list containing `1`, `2`, and `3`. `'abc'` is a list
-  //. containing `'a'`, `'b'`, and `'c'`. But what is the type of `'a'`?
-  //. `String`, since JavaScript has no Char type! Thus:
-  //.
-  //.     'abc' :: String, List String, List (List String), ...
-  //.
-  //. Every member of `String` is also a member of `List String`!
+  //. ### Array
 
-  //# slice :: Integer -> Integer -> List a -> Maybe (List a)
+  //# slice :: Integer -> Integer -> Array a -> Maybe (Array a)
   //.
-  //. Returns Just a list containing the elements from the supplied list
-  //. from a beginning index (inclusive) to an end index (exclusive).
-  //. Returns Nothing unless the start interval is less than or equal to
-  //. the end interval, and the list contains both (half-open) intervals.
-  //. Accepts negative indices, which indicate an offset from the end of
-  //. the list.
+  //. Takes a start index `i`, an end index `j`, and an array, and returns
+  //. Just the `[i,j)` slice of the array if possible; Nothing otherwise.
+  //. A negative index represents an offset from the length of the array.
   //.
   //. See also [`take`](#take), [`drop`](#drop), [`takeLast`](#takeLast),
   //. and [`dropLast`](#dropLast).
@@ -3301,9 +3277,6 @@
   //.
   //. > S.slice(1, 6, ['a', 'b', 'c', 'd', 'e'])
   //. Nothing
-  //.
-  //. > S.slice(2, 6, 'banana')
-  //. Just('nana')
   //. ```
   function slice(start, end, xs) {
     var len = xs.length;
@@ -3316,15 +3289,15 @@
   }
   _.slice = {
     consts: {},
-    types: [$.Integer, $.Integer, List(a), $Maybe(List(a))],
+    types: [$.Integer, $.Integer, $.Array(a), $Maybe($.Array(a))],
     impl: slice
   };
 
-  //# at :: Integer -> List a -> Maybe a
+  //# at :: Integer -> Array a -> Maybe a
   //.
-  //. Takes an index and a list and returns Just the element of the list at
-  //. the index if the index is within the list's bounds; Nothing otherwise.
-  //. A negative index represents an offset from the length of the list.
+  //. Returns Just the element of the given array at the specified index if
+  //. the index is within the array's bounds; Nothing otherwise. A negative
+  //. index represents an offset from the length of the array.
   //.
   //. ```javascript
   //. > S.at(2, ['a', 'b', 'c', 'd', 'e'])
@@ -3342,14 +3315,14 @@
   }
   _.at = {
     consts: {},
-    types: [$.Integer, List(a), $Maybe(a)],
+    types: [$.Integer, $.Array(a), $Maybe(a)],
     impl: at
   };
 
-  //# head :: List a -> Maybe a
+  //# head :: Array a -> Maybe a
   //.
-  //. Takes a list and returns Just the first element of the list if the
-  //. list contains at least one element; Nothing if the list is empty.
+  //. Returns Just the first element of the given array if the array contains
+  //. at least one element; Nothing otherwise.
   //.
   //. ```javascript
   //. > S.head([1, 2, 3])
@@ -3359,18 +3332,18 @@
   //. Nothing
   //. ```
   function head(xs) {
-    return at(0, xs);
+    return xs.length > 0 ? Just(xs[0]) : Nothing;
   }
   _.head = {
     consts: {},
-    types: [List(a), $Maybe(a)],
+    types: [$.Array(a), $Maybe(a)],
     impl: head
   };
 
-  //# last :: List a -> Maybe a
+  //# last :: Array a -> Maybe a
   //.
-  //. Takes a list and returns Just the last element of the list if the
-  //. list contains at least one element; Nothing if the list is empty.
+  //. Returns Just the last element of the given array if the array contains
+  //. at least one element; Nothing otherwise.
   //.
   //. ```javascript
   //. > S.last([1, 2, 3])
@@ -3380,19 +3353,18 @@
   //. Nothing
   //. ```
   function last(xs) {
-    return at(-1, xs);
+    return xs.length > 0 ? Just(xs[xs.length - 1]) : Nothing;
   }
   _.last = {
     consts: {},
-    types: [List(a), $Maybe(a)],
+    types: [$.Array(a), $Maybe(a)],
     impl: last
   };
 
-  //# tail :: List a -> Maybe (List a)
+  //# tail :: Array a -> Maybe (Array a)
   //.
-  //. Takes a list and returns Just a list containing all but the first
-  //. of the list's elements if the list contains at least one element;
-  //. Nothing if the list is empty.
+  //. Returns Just all but the first of the given array's elements if the
+  //. array contains at least one element; Nothing otherwise.
   //.
   //. ```javascript
   //. > S.tail([1, 2, 3])
@@ -3406,15 +3378,14 @@
   }
   _.tail = {
     consts: {},
-    types: [List(a), $Maybe(List(a))],
+    types: [$.Array(a), $Maybe($.Array(a))],
     impl: tail
   };
 
-  //# init :: List a -> Maybe (List a)
+  //# init :: Array a -> Maybe (Array a)
   //.
-  //. Takes a list and returns Just a list containing all but the last
-  //. of the list's elements if the list contains at least one element;
-  //. Nothing if the list is empty.
+  //. Returns Just all but the last of the given array's elements if the
+  //. array contains at least one element; Nothing otherwise.
   //.
   //. ```javascript
   //. > S.init([1, 2, 3])
@@ -3428,112 +3399,111 @@
   }
   _.init = {
     consts: {},
-    types: [List(a), $Maybe(List(a))],
+    types: [$.Array(a), $Maybe($.Array(a))],
     impl: init
   };
 
-  //# take :: Integer -> List a -> Maybe (List a)
+  //# take :: Integer -> Array a -> Maybe (Array a)
   //.
-  //. Returns Just the first N elements of the given collection if N is
-  //. greater than or equal to zero and less than or equal to the length
-  //. of the collection; Nothing otherwise.
+  //. Returns Just the first N elements of the given array if N is greater
+  //. than or equal to zero and less than or equal to the length of the array;
+  //. Nothing otherwise.
   //.
   //. ```javascript
   //. > S.take(2, ['a', 'b', 'c', 'd', 'e'])
   //. Just(['a', 'b'])
   //.
-  //. > S.take(4, 'abcdefg')
-  //. Just('abcd')
+  //. > S.take(5, ['a', 'b', 'c', 'd', 'e'])
+  //. Just(['a', 'b', 'c', 'd', 'e'])
   //.
-  //. > S.take(4, ['a', 'b', 'c'])
+  //. > S.take(6, ['a', 'b', 'c', 'd', 'e'])
   //. Nothing
   //. ```
   function take(n, xs) {
-    return n < 0 || n > xs.length ? Nothing : Just(xs.slice(0, n));
+    return n >= 0 && n <= xs.length ? Just(xs.slice(0, n)) : Nothing;
   }
   _.take = {
     consts: {},
-    types: [$.Integer, List(a), $Maybe(List(a))],
+    types: [$.Integer, $.Array(a), $Maybe($.Array(a))],
     impl: take
   };
 
-  //# takeLast :: Integer -> List a -> Maybe (List a)
+  //# takeLast :: Integer -> Array a -> Maybe (Array a)
   //.
-  //. Returns Just the last N elements of the given collection if N is
-  //. greater than or equal to zero and less than or equal to the length
-  //. of the collection; Nothing otherwise.
+  //. Returns Just the last N elements of the given array if N is greater
+  //. than or equal to zero and less than or equal to the length of the array;
+  //. Nothing otherwise.
   //.
   //. ```javascript
   //. > S.takeLast(2, ['a', 'b', 'c', 'd', 'e'])
   //. Just(['d', 'e'])
   //.
-  //. > S.takeLast(4, 'abcdefg')
-  //. Just('defg')
+  //. > S.takeLast(5, ['a', 'b', 'c', 'd', 'e'])
+  //. Just(['a', 'b', 'c', 'd', 'e'])
   //.
-  //. > S.takeLast(4, ['a', 'b', 'c'])
+  //. > S.takeLast(6, ['a', 'b', 'c', 'd', 'e'])
   //. Nothing
   //. ```
   function takeLast(n, xs) {
-    return n < 0 || n > xs.length ? Nothing : Just(xs.slice(xs.length - n));
+    return n >= 0 && n <= xs.length ? Just(xs.slice(xs.length - n)) : Nothing;
   }
   _.takeLast = {
     consts: {},
-    types: [$.Integer, List(a), $Maybe(List(a))],
+    types: [$.Integer, $.Array(a), $Maybe($.Array(a))],
     impl: takeLast
   };
 
-  //# drop :: Integer -> List a -> Maybe (List a)
+  //# drop :: Integer -> Array a -> Maybe (Array a)
   //.
-  //. Returns Just all but the first N elements of the given collection
-  //. if N is greater than or equal to zero and less than or equal to the
-  //. length of the collection; Nothing otherwise.
+  //. Returns Just all but the first N elements of the given array if N is
+  //. greater than or equal to zero and less than or equal to the length of
+  //. the array; Nothing otherwise.
   //.
   //. ```javascript
   //. > S.drop(2, ['a', 'b', 'c', 'd', 'e'])
   //. Just(['c', 'd', 'e'])
   //.
-  //. > S.drop(4, 'abcdefg')
-  //. Just('efg')
+  //. > S.drop(5, ['a', 'b', 'c', 'd', 'e'])
+  //. Just([])
   //.
-  //. > S.drop(4, 'abc')
+  //. > S.drop(6, ['a', 'b', 'c', 'd', 'e'])
   //. Nothing
   //. ```
   function drop(n, xs) {
-    return n < 0 || n > xs.length ? Nothing : Just(xs.slice(n));
+    return n >= 0 && n <= xs.length ? Just(xs.slice(n)) : Nothing;
   }
   _.drop = {
     consts: {},
-    types: [$.Integer, List(a), $Maybe(List(a))],
+    types: [$.Integer, $.Array(a), $Maybe($.Array(a))],
     impl: drop
   };
 
-  //# dropLast :: Integer -> List a -> Maybe (List a)
+  //# dropLast :: Integer -> Array a -> Maybe (Array a)
   //.
-  //. Returns Just all but the last N elements of the given collection
-  //. if N is greater than or equal to zero and less than or equal to the
-  //. length of the collection; Nothing otherwise.
+  //. Returns Just all but the last N elements of the given array if N is
+  //. greater than or equal to zero and less than or equal to the length of
+  //. the array; Nothing otherwise.
   //.
   //. ```javascript
   //. > S.dropLast(2, ['a', 'b', 'c', 'd', 'e'])
   //. Just(['a', 'b', 'c'])
   //.
-  //. > S.dropLast(4, 'abcdefg')
-  //. Just('abc')
+  //. > S.dropLast(5, ['a', 'b', 'c', 'd', 'e'])
+  //. Just([])
   //.
-  //. > S.dropLast(4, 'abc')
+  //. > S.dropLast(6, ['a', 'b', 'c', 'd', 'e'])
   //. Nothing
   //. ```
   function dropLast(n, xs) {
-    return n < 0 || n > xs.length ? Nothing : Just(xs.slice(0, xs.length - n));
+    return n >= 0 && n <= xs.length ? Just(xs.slice(0, xs.length - n))
+                                    : Nothing;
   }
   _.dropLast = {
     consts: {},
-    types: [$.Integer, List(a), $Maybe(List(a))],
+    types: [$.Integer, $.Array(a), $Maybe($.Array(a))],
     impl: dropLast
   };
 
-  //. ### Array
-
   //# size :: Foldable f => f a -> Integer
   //.
   //. Returns the number of elements of the given structure.
@@ -5005,8 +4975,6 @@
 
 }));
 
-//. [$.Array]:          v:sanctuary-js/sanctuary-def#Array
-//. [$.String]:         v:sanctuary-js/sanctuary-def#String
 //. [Alt]:              v:fantasyland/fantasy-land#alt
 //. [Alternative]:      v:fantasyland/fantasy-land#alternative
 //. [Applicative]:      v:fantasyland/fantasy-land#applicative


Test patch:
diff --git a/test/Maybe/Maybe.js b/test/Maybe/Maybe.js
index dd9e66e..0b18d01 100644
--- a/test/Maybe/Maybe.js
+++ b/test/Maybe/Maybe.js
@@ -128,7 +128,7 @@ suite('Maybe', function() {
 
     monadLaws.leftIdentity(
       jsc.constant(S.head),
-      jsc.string
+      jsc.array(jsc.number)
     );
 
     monadLaws.rightIdentity(
diff --git a/test/at.js b/test/at.js
index e284529..c25de28 100644
--- a/test/at.js
+++ b/test/at.js
@@ -9,7 +9,7 @@ test('at', function() {
 
   eq(typeof S.at, 'function');
   eq(S.at.length, 2);
-  eq(S.at.toString(), 'at :: Integer -> List a -> Maybe a');
+  eq(S.at.toString(), 'at :: Integer -> Array a -> Maybe a');
 
   eq(S.at(-4, ['foo', 'bar', 'baz']), S.Nothing);
   eq(S.at(-3, ['foo', 'bar', 'baz']), S.Just('foo'));
diff --git a/test/drop.js b/test/drop.js
index ca8ffff..931882d 100644
--- a/test/drop.js
+++ b/test/drop.js
@@ -9,7 +9,7 @@ test('drop', function() {
 
   eq(typeof S.drop, 'function');
   eq(S.drop.length, 2);
-  eq(S.drop.toString(), 'drop :: Integer -> List a -> Maybe (List a)');
+  eq(S.drop.toString(), 'drop :: Integer -> Array a -> Maybe (Array a)');
 
   eq(S.drop(0, [1, 2, 3, 4, 5]), S.Just([1, 2, 3, 4, 5]));
   eq(S.drop(1, [1, 2, 3, 4, 5]), S.Just([2, 3, 4, 5]));
@@ -19,14 +19,6 @@ test('drop', function() {
   eq(S.drop(5, [1, 2, 3, 4, 5]), S.Just([]));
   eq(S.drop(6, [1, 2, 3, 4, 5]), S.Nothing);
 
-  eq(S.drop(0, '12345'), S.Just('12345'));
-  eq(S.drop(1, '12345'), S.Just('2345'));
-  eq(S.drop(2, '12345'), S.Just('345'));
-  eq(S.drop(3, '12345'), S.Just('45'));
-  eq(S.drop(4, '12345'), S.Just('5'));
-  eq(S.drop(5, '12345'), S.Just(''));
-  eq(S.drop(6, '12345'), S.Nothing);
-
   eq(S.drop(-1, [1, 2, 3, 4, 5]), S.Nothing);
 
 });
diff --git a/test/dropLast.js b/test/dropLast.js
index e8cbc61..2cef5b7 100644
--- a/test/dropLast.js
+++ b/test/dropLast.js
@@ -9,7 +9,7 @@ test('dropLast', function() {
 
   eq(typeof S.dropLast, 'function');
   eq(S.dropLast.length, 2);
-  eq(S.dropLast.toString(), 'dropLast :: Integer -> List a -> Maybe (List a)');
+  eq(S.dropLast.toString(), 'dropLast :: Integer -> Array a -> Maybe (Array a)');
 
   eq(S.dropLast(0, [1, 2, 3, 4, 5]), S.Just([1, 2, 3, 4, 5]));
   eq(S.dropLast(1, [1, 2, 3, 4, 5]), S.Just([1, 2, 3, 4]));
@@ -19,14 +19,6 @@ test('dropLast', function() {
   eq(S.dropLast(5, [1, 2, 3, 4, 5]), S.Just([]));
   eq(S.dropLast(6, [1, 2, 3, 4, 5]), S.Nothing);
 
-  eq(S.dropLast(0, '12345'), S.Just('12345'));
-  eq(S.dropLast(1, '12345'), S.Just('1234'));
-  eq(S.dropLast(2, '12345'), S.Just('123'));
-  eq(S.dropLast(3, '12345'), S.Just('12'));
-  eq(S.dropLast(4, '12345'), S.Just('1'));
-  eq(S.dropLast(5, '12345'), S.Just(''));
-  eq(S.dropLast(6, '12345'), S.Nothing);
-
   eq(S.dropLast(-1, [1, 2, 3, 4, 5]), S.Nothing);
 
 });
diff --git a/test/head.js b/test/head.js
index 41b4339..729d6be 100644
--- a/test/head.js
+++ b/test/head.js
@@ -9,7 +9,7 @@ test('head', function() {
 
   eq(typeof S.head, 'function');
   eq(S.head.length, 1);
-  eq(S.head.toString(), 'head :: List a -> Maybe a');
+  eq(S.head.toString(), 'head :: Array a -> Maybe a');
 
   eq(S.head([]), S.Nothing);
   eq(S.head(['foo']), S.Just('foo'));
diff --git a/test/init.js b/test/init.js
index 43d15df..4b97fe2 100644
--- a/test/init.js
+++ b/test/init.js
@@ -9,7 +9,7 @@ test('init', function() {
 
   eq(typeof S.init, 'function');
   eq(S.init.length, 1);
-  eq(S.init.toString(), 'init :: List a -> Maybe (List a)');
+  eq(S.init.toString(), 'init :: Array a -> Maybe (Array a)');
 
   eq(S.init([]), S.Nothing);
   eq(S.init(['foo']), S.Just([]));
diff --git a/test/last.js b/test/last.js
index 3bce893..3ba35df 100644
--- a/test/last.js
+++ b/test/last.js
@@ -9,7 +9,7 @@ test('last', function() {
 
   eq(typeof S.last, 'function');
   eq(S.last.length, 1);
-  eq(S.last.toString(), 'last :: List a -> Maybe a');
+  eq(S.last.toString(), 'last :: Array a -> Maybe a');
 
   eq(S.last([]), S.Nothing);
   eq(S.last(['foo']), S.Just('foo'));
diff --git a/test/slice.js b/test/slice.js
index d2b8a1b..460b00a 100644
--- a/test/slice.js
+++ b/test/slice.js
@@ -9,7 +9,7 @@ test('slice', function() {
 
   eq(typeof S.slice, 'function');
   eq(S.slice.length, 3);
-  eq(S.slice.toString(), 'slice :: Integer -> Integer -> List a -> Maybe (List a)');
+  eq(S.slice.toString(), 'slice :: Integer -> Integer -> Array a -> Maybe (Array a)');
 
   eq(S.slice(6, 1, [1, 2, 3, 4, 5]), S.Nothing);
   eq(S.slice(1, 6, [1, 2, 3, 4, 5]), S.Nothing);
@@ -28,8 +28,4 @@ test('slice', function() {
 
   eq(S.slice(0, 5, [1, 2, 3, 4, 5]), S.Just([1, 2, 3, 4, 5]));
 
-  eq(S.slice(1, -3, 'abcde'), S.Just('b'));
-  eq(S.slice(2, -3, 'abcde'), S.Just(''));
-  eq(S.slice(3, -3, 'abcde'), S.Nothing);
-
 });
diff --git a/test/tail.js b/test/tail.js
index 874defe..04f040b 100644
--- a/test/tail.js
+++ b/test/tail.js
@@ -9,7 +9,7 @@ test('tail', function() {
 
   eq(typeof S.tail, 'function');
   eq(S.tail.length, 1);
-  eq(S.tail.toString(), 'tail :: List a -> Maybe (List a)');
+  eq(S.tail.toString(), 'tail :: Array a -> Maybe (Array a)');
 
   eq(S.tail([]), S.Nothing);
   eq(S.tail(['foo']), S.Just([]));
diff --git a/test/take.js b/test/take.js
index 2e965f5..d371d4e 100644
--- a/test/take.js
+++ b/test/take.js
@@ -9,7 +9,7 @@ test('take', function() {
 
   eq(typeof S.take, 'function');
   eq(S.take.length, 2);
-  eq(S.take.toString(), 'take :: Integer -> List a -> Maybe (List a)');
+  eq(S.take.toString(), 'take :: Integer -> Array a -> Maybe (Array a)');
 
   eq(S.take(0, [1, 2, 3, 4, 5]), S.Just([]));
   eq(S.take(1, [1, 2, 3, 4, 5]), S.Just([1]));
@@ -19,14 +19,6 @@ test('take', function() {
   eq(S.take(5, [1, 2, 3, 4, 5]), S.Just([1, 2, 3, 4, 5]));
   eq(S.take(6, [1, 2, 3, 4, 5]), S.Nothing);
 
-  eq(S.take(0, '12345'), S.Just(''));
-  eq(S.take(1, '12345'), S.Just('1'));
-  eq(S.take(2, '12345'), S.Just('12'));
-  eq(S.take(3, '12345'), S.Just('123'));
-  eq(S.take(4, '12345'), S.Just('1234'));
-  eq(S.take(5, '12345'), S.Just('12345'));
-  eq(S.take(6, '12345'), S.Nothing);
-
   eq(S.take(-1, [1, 2, 3, 4, 5]), S.Nothing);
 
 });
diff --git a/test/takeLast.js b/test/takeLast.js
index e275b34..8dc7163 100644
--- a/test/takeLast.js
+++ b/test/takeLast.js
@@ -9,7 +9,7 @@ test('takeLast', function() {
 
   eq(typeof S.takeLast, 'function');
   eq(S.takeLast.length, 2);
-  eq(S.takeLast.toString(), 'takeLast :: Integer -> List a -> Maybe (List a)');
+  eq(S.takeLast.toString(), 'takeLast :: Integer -> Array a -> Maybe (Array a)');
 
   eq(S.takeLast(0, [1, 2, 3, 4, 5]), S.Just([]));
   eq(S.takeLast(1, [1, 2, 3, 4, 5]), S.Just([5]));
@@ -19,14 +19,6 @@ test('takeLast', function() {
   eq(S.takeLast(5, [1, 2, 3, 4, 5]), S.Just([1, 2, 3, 4, 5]));
   eq(S.takeLast(6, [1, 2, 3, 4, 5]), S.Nothing);
 
-  eq(S.takeLast(0, '12345'), S.Just(''));
-  eq(S.takeLast(1, '12345'), S.Just('5'));
-  eq(S.takeLast(2, '12345'), S.Just('45'));
-  eq(S.takeLast(3, '12345'), S.Just('345'));
-  eq(S.takeLast(4, '12345'), S.Just('2345'));
-  eq(S.takeLast(5, '12345'), S.Just('12345'));
-  eq(S.takeLast(6, '12345'), S.Nothing);
-
   eq(S.takeLast(-1, [1, 2, 3, 4, 5]), S.Nothing);
 
 });