Repository: plaid/sanctuary
Pull request #15 (2015-04-15 22:08:02)

report lengths of exported functions correctly
The internal `arity` function uses the `Function` constructor to avoid the [wall of text][1] used to define `R.arity`.


[1]: https://github.com/ramda/ramda/blob/v0.13.0/src/arity.js#L32-L43



Patch:
diff --git a/index.js b/index.js
index ec05c85..1525479 100644
--- a/index.js
+++ b/index.js
@@ -20,15 +20,29 @@
     return toString_.call(x) === '[object Array]';
   };
 
+  var arity = function(n, f) {
+    var params = Array(n + 1).join('x').replace(/x/g, function(_, n) {
+      return n === 0 ? '$0' : ', $' + n;
+    });
+    return Function(  // jshint ignore:line
+      'f',
+      'return function(' + params + ') { return f.apply(this, arguments); }'
+    ).call(this, f);
+  };
+
   var curry = function(f) {
-    var arity = f.length;
-    var recurry = function(args) {
-      return function() {
-        var args2 = args.concat(slice_.call(arguments));
-        return args2.length >= arity ? f.apply(this, args2) : recurry(args2);
-      };
-    };
-    return recurry([]);
+    var len = f.length;
+    return arity(len, function() {
+      var shortfall = len - arguments.length;
+      if (shortfall <= 0) {
+        return f.apply(this, arguments);
+      } else {
+        var args = slice_.call(arguments);
+        return curry(arity(shortfall, function() {
+          return f.apply(this, args.concat(slice_.call(arguments)));
+        }));
+      }
+    });
   };
 
   var extend = function(Child, Parent) {
@@ -168,13 +182,13 @@
 
   //  encase :: (* -> a) -> (* -> Maybe a)
   var encase = function(f) {
-    return function() {
+    return arity(f.length, function() {
       try {
         return Just(f.apply(this, arguments));
       } catch (err) {
         return Nothing();
       }
-    };
+    });
   };
 
   //  either  ////////////////////////////////////////////////////////////////
@@ -320,7 +334,9 @@
   });
 
   //  parseJson :: String -> Maybe *
-  var parseJson = encase(JSON.parse);
+  var parseJson = encase(function(s) {
+    return JSON.parse(s);
+  });
 
   //  exports  ///////////////////////////////////////////////////////////////
 


Test patch:
diff --git a/test/index.js b/test/index.js
index 055e9a2..d14e3b7 100644
--- a/test/index.js
+++ b/test/index.js
@@ -381,6 +381,11 @@ describe('maybe', function() {
 
   describe('fromMaybe', function() {
 
+    it('is a binary function', function() {
+      eq(typeof S.fromMaybe, 'function');
+      eq(S.fromMaybe.length, 2);
+    });
+
     it('can be applied to a Nothing', function() {
       eq(S.fromMaybe(0, S.Nothing()), 0);
     });
@@ -407,6 +412,11 @@ describe('maybe', function() {
 
   describe('toMaybe', function() {
 
+    it('is a unary function', function() {
+      eq(typeof S.toMaybe, 'function');
+      eq(S.toMaybe.length, 1);
+    });
+
     it('returns a Nothing when applied to null/undefined', function() {
       assert(S.toMaybe(null).equals(S.Nothing()));
       assert(S.toMaybe(undefined).equals(S.Nothing()));
@@ -421,6 +431,11 @@ describe('maybe', function() {
 
   describe('encase', function() {
 
+    it('is a unary function', function() {
+      eq(typeof S.encase, 'function');
+      eq(S.encase.length, 1);
+    });
+
     //  factorial :: Number -> Number
     var factorial = function(n) {
       if (n < 0) {
@@ -445,6 +460,11 @@ describe('maybe', function() {
       assert(f(1, 2, 3, 4, 5).equals(S.Just(5)));
     });
 
+    it('returns a function of appropriate arity', function() {
+      var f = S.encase(function(a, b, c, d, e) { return e; });
+      eq(f.length, 5);
+    });
+
     it('preserves context', function() {
       var f = S.encase(function() { return this; });
       var ctx = {};
@@ -551,6 +571,11 @@ describe('either', function() {
 
   describe('either', function() {
 
+    it('is a ternary function', function() {
+      eq(typeof S.either, 'function');
+      eq(S.either.length, 3);
+    });
+
     it('can be applied to a Left', function() {
       eq(S.either(length, square, S.Left('abc')), 3);
     });
@@ -593,6 +618,11 @@ describe('control', function() {
     var just = S.Just(42);
     var just2 = S.Just(42);
 
+    it('is a binary function', function() {
+      eq(typeof S.or, 'function');
+      eq(S.or.length, 2);
+    });
+
     it('can be applied to arrays', function() {
       eq(S.or(empty, empty2), empty2);
       eq(S.or(empty, nonempty), nonempty);
@@ -651,6 +681,11 @@ describe('list', function() {
 
   describe('at', function() {
 
+    it('is a binary function', function() {
+      eq(typeof S.at, 'function');
+      eq(S.at.length, 2);
+    });
+
     it('returns Just the nth element of a list', function() {
       assert(S.at(1, ['foo', 'bar', 'baz']).equals(S.Just('bar')));
     });
@@ -672,6 +707,11 @@ describe('list', function() {
 
   describe('head', function() {
 
+    it('is a unary function', function() {
+      eq(typeof S.head, 'function');
+      eq(S.head.length, 1);
+    });
+
     it('returns a Nothing if applied to empty list', function() {
       assert(S.head([]).equals(S.Nothing()));
     });
@@ -684,6 +724,11 @@ describe('list', function() {
 
   describe('last', function() {
 
+    it('is a unary function', function() {
+      eq(typeof S.last, 'function');
+      eq(S.last.length, 1);
+    });
+
     it('returns a Nothing if applied to empty list', function() {
       assert(S.last([]).equals(S.Nothing()));
     });
@@ -696,6 +741,11 @@ describe('list', function() {
 
   describe('tail', function() {
 
+    it('is a unary function', function() {
+      eq(typeof S.tail, 'function');
+      eq(S.tail.length, 1);
+    });
+
     it('returns a Nothing if applied to empty list', function() {
       assert(S.tail([]).equals(S.Nothing()));
     });
@@ -710,6 +760,11 @@ describe('list', function() {
 
   describe('init', function() {
 
+    it('is a unary function', function() {
+      eq(typeof S.init, 'function');
+      eq(S.init.length, 1);
+    });
+
     it('returns a Nothing if applied to empty list', function() {
       assert(S.init([]).equals(S.Nothing()));
     });
@@ -724,6 +779,11 @@ describe('list', function() {
 
   describe('find', function() {
 
+    it('is a binary function', function() {
+      eq(typeof S.find, 'function');
+      eq(S.find.length, 2);
+    });
+
     it('returns Just the first element satisfying the predicate', function() {
       assert(S.find(T, [null]).equals(S.Just(null)));
       assert(S.find(function(n) { return n >= 0; }, [-1, 0, 1])
@@ -747,6 +807,11 @@ describe('object', function() {
 
   describe('get', function() {
 
+    it('is a binary function', function() {
+      eq(typeof S.get, 'function');
+      eq(S.get.length, 2);
+    });
+
     it('returns a Maybe', function() {
       var obj = {x: 0, y: 42};
       assert(S.get('x', obj).equals(S.Just(0)));
@@ -774,6 +839,11 @@ describe('parse', function() {
 
   describe('parseDate', function() {
 
+    it('is a unary function', function() {
+      eq(typeof S.parseDate, 'function');
+      eq(S.parseDate.length, 1);
+    });
+
     it('returns a Just when applied to a valid date string', function() {
       var maybe = S.parseDate('2001-02-03T04:05:06Z');
       eq(maybe.constructor, S.Just);
@@ -790,6 +860,11 @@ describe('parse', function() {
 
   describe('parseFloat', function() {
 
+    it('is a unary function', function() {
+      eq(typeof S.parseFloat, 'function');
+      eq(S.parseFloat.length, 1);
+    });
+
     it('returns a Maybe', function() {
       assert(S.parseFloat('12.34').equals(S.Just(12.34)));
       assert(S.parseFloat('xxx').equals(S.Nothing()));
@@ -799,6 +874,11 @@ describe('parse', function() {
 
   describe('parseInt', function() {
 
+    it('is a binary function', function() {
+      eq(typeof S.parseInt, 'function');
+      eq(S.parseInt.length, 2);
+    });
+
     it('returns a Maybe', function() {
       assert(S.parseInt(10, '42').equals(S.Just(42)));
       assert(S.parseInt(16, '2A').equals(S.Just(42)));
@@ -813,6 +893,11 @@ describe('parse', function() {
 
   describe('parseJson', function() {
 
+    it('is a unary function', function() {
+      eq(typeof S.parseJson, 'function');
+      eq(S.parseJson.length, 1);
+    });
+
     it('returns a Just when applied to a valid JSON string', function() {
       var xs = S.fromMaybe(null, S.parseJson('["foo","bar"]'));
       eq(Object.prototype.toString.call(xs), '[object Array]');