Repository: sanctuary-js/sanctuary (js)

Issue:
Add S.prepend function
Create a typesafe version of [R.prepend](http://ramdajs.com/docs/#prepend).

Pull request description:
array: add S.prepend
Closes #197


Patch:
diff --git a/index.js b/index.js
index b57030b..7ca1f21 100644
--- a/index.js
+++ b/index.js
@@ -2451,12 +2451,11 @@
   //. Takes a value of any type and an array of values of that type, and
   //. returns the result of appending the value to the array.
   //.
+  //. See also [`prepend`](#prepend).
+  //.
   //. ```javascript
   //. > S.append(3, [1, 2])
   //. [1, 2, 3]
-  //.
-  //. > S.append([3, 4], [[1], [2]])
-  //. [[1], [2], [3, 4]]
   //. ```
   S.append =
   def('append',
@@ -2464,6 +2463,23 @@
       [a, $.Array(a), $.Array(a)],
       function(x, xs) { return xs.concat([x]); });
 
+  //# prepend :: a -> Array a -> Array a
+  //.
+  //. Takes a value of any type and an array of values of that type, and
+  //. returns the result of prepending the value to the array.
+  //.
+  //. See also [`append`](#append).
+  //.
+  //. ```javascript
+  //. > S.prepend(1, [2, 3])
+  //. [1, 2, 3]
+  //. ```
+  S.prepend =
+  def('prepend',
+      {},
+      [a, $.Array(a), $.Array(a)],
+      function(x, xs) { return [x].concat(xs); });
+
   //# find :: (a -> Boolean) -> Array a -> Maybe a
   //.
   //. Takes a predicate and an array and returns Just the leftmost element of


Test patch:
diff --git a/test/append.js b/test/append.js
index c06717d..c2b1d2e 100644
--- a/test/append.js
+++ b/test/append.js
@@ -27,7 +27,7 @@ describe('append', function() {
                    '\n' +
                    'The value at position 1 is not a member of ‘Array a’.\n'));
 
-    throws(function() { S.append('c', [1, 2]); },
+    throws(function() { S.append('3', [1, 2]); },
            errorEq(TypeError,
                    'Type-variable constraint violation\n' +
                    '\n' +
@@ -35,7 +35,7 @@ describe('append', function() {
                    '          ^          ^\n' +
                    '          1          2\n' +
                    '\n' +
-                   '1)  "c" :: String\n' +
+                   '1)  "3" :: String\n' +
                    '\n' +
                    '2)  1 :: Number, FiniteNumber, NonZeroFiniteNumber, Integer, ValidNumber\n' +
                    '    2 :: Number, FiniteNumber, NonZeroFiniteNumber, Integer, ValidNumber\n' +
@@ -43,22 +43,15 @@ describe('append', function() {
                    'Since there is no type of which all the above values are members, the type-variable constraint has been violated.\n'));
   });
 
-  it('adds the element to the end of the list', function() {
-    eq(S.append('c', ['a', 'b']), ['a', 'b', 'c']);
-    eq(S.append({x: 3}, [{x: 1}, {x: 2}]), [{x: 1}, {x: 2}, {x: 3}]);
-  });
-
-  it('adds a list to a list of lists', function() {
-    eq(S.append([3, 4], [[1], [2]]), [[1], [2], [3, 4]]);
-  });
-
-  it('works on empty list', function() {
-    eq(S.append(1, []), [1]);
+  it('appends an element to an array', function() {
+    eq(S.append(3, []), [3]);
+    eq(S.append(3, [1, 2]), [1, 2, 3]);
+    eq(S.append([5, 6], [[1, 2], [3, 4]]), [[1, 2], [3, 4], [5, 6]]);
   });
 
   it('is curried', function() {
-    eq(S.append('c').length, 1);
-    eq(S.append('c')(['a', 'b']), ['a', 'b', 'c']);
+    eq(S.append(3).length, 1);
+    eq(S.append(3)([1, 2]), [1, 2, 3]);
   });
 
 });
diff --git a/test/prepend.js b/test/prepend.js
new file mode 100644
index 0000000..74e1e1b
--- /dev/null
+++ b/test/prepend.js
@@ -0,0 +1,57 @@
+'use strict';
+
+var throws = require('assert').throws;
+
+var errorEq = require('./utils').errorEq;
+var eq = require('./utils').eq;
+var S = require('..');
+
+
+describe('prepend', function() {
+
+  it('is a binary function', function() {
+    eq(typeof S.prepend, 'function');
+    eq(S.prepend.length, 2);
+  });
+
+  it('type checks its arguments', function() {
+    throws(function() { S.prepend('a', 'bc'); },
+           errorEq(TypeError,
+                   'Invalid value\n' +
+                   '\n' +
+                   'prepend :: a -> Array a -> Array a\n' +
+                   '                ^^^^^^^\n' +
+                   '                   1\n' +
+                   '\n' +
+                   '1)  "bc" :: String\n' +
+                   '\n' +
+                   'The value at position 1 is not a member of ‘Array a’.\n'));
+
+    throws(function() { S.prepend('1', [2, 3]); },
+           errorEq(TypeError,
+                   'Type-variable constraint violation\n' +
+                   '\n' +
+                   'prepend :: a -> Array a -> Array a\n' +
+                   '           ^          ^\n' +
+                   '           1          2\n' +
+                   '\n' +
+                   '1)  "1" :: String\n' +
+                   '\n' +
+                   '2)  2 :: Number, FiniteNumber, NonZeroFiniteNumber, Integer, ValidNumber\n' +
+                   '    3 :: Number, FiniteNumber, NonZeroFiniteNumber, Integer, ValidNumber\n' +
+                   '\n' +
+                   'Since there is no type of which all the above values are members, the type-variable constraint has been violated.\n'));
+  });
+
+  it('prepends an element to an array', function() {
+    eq(S.prepend(1, []), [1]);
+    eq(S.prepend(1, [2, 3]), [1, 2, 3]);
+    eq(S.prepend([1, 2], [[3, 4], [5, 6]]), [[1, 2], [3, 4], [5, 6]]);
+  });
+
+  it('is curried', function() {
+    eq(S.prepend(1).length, 1);
+    eq(S.prepend(1)([2, 3]), [1, 2, 3]);
+  });
+
+});