Repository: plaid/sanctuary
Pull request #54 (2015-06-17 18:25:58)

either: define extend
Defines an `extend` method for the `Either` type as per the [fantasyland](https://github.com/fantasyland/fantasy-land#extend) spec.


Patch:
diff --git a/index.js b/index.js
index f183dc3..c1a3677 100644
--- a/index.js
+++ b/index.js
@@ -635,6 +635,23 @@
   //. false
   //. ```
 
+  //# Either#extend :: Either a b ~> (b -> b) -> Either a b
+  //.
+  //. Takes a function and returns `this` if `this` is a Left; otherwise it
+  //. returns a Right whose value is the result of applying the function to
+  //. this Right's value. `Either#extend` is a restricted form of
+  //. [`Either#map`](#Either.prototype.map): the function provided must
+  //. return a value of the same type as its input (`Either#map` does not
+  //. have this restriction).
+  //.
+  //. ```javascript
+  //. > S.Left('Cannot divide by zero').extend(R.inc)
+  //. Left("Cannot divide by zero")
+  //.
+  //. > S.Right(42).extend(R.inc)
+  //. Right(43)
+  //. ```
+
   //# Either#map :: Either a b ~> (b -> c) -> Either a c
   //.
   //. Takes a function and returns `this` if `this` is a Left; otherwise it
@@ -645,8 +662,8 @@
   //. > S.Left('Cannot divide by zero').map(R.inc)
   //. Left("Cannot divide by zero")
   //.
-  //. > S.Right(42).map(R.inc)
-  //. Right(43)
+  //. > S.Right([1, 2, 3]).map(R.sum)
+  //. Right(6)
   //. ```
 
   //# Either#of :: Either a b ~> b -> Either a b
@@ -724,6 +741,9 @@
     return x instanceof Left && R.eqProps('value', x, this);
   });
 
+  //  Left#extend :: Either a b ~> (b -> b) -> Either a b
+  Left.prototype.extend = def('Left#extend', [Function], self);
+
   //  Left#map :: Either a b ~> (b -> c) -> Either a c
   Left.prototype.map = def('Left#map', [Function], self);
 
@@ -771,6 +791,11 @@
     return x instanceof Right && R.eqProps('value', x, this);
   });
 
+  //  Right#extend :: Either a b ~> (b -> b) -> Either a b
+  Right.prototype.extend = def('Right#extend', [Function], function(f) {
+    return Right(f(this.value));
+  });
+
   //  Right#map :: Either a b ~> (b -> c) -> Either a c
   Right.prototype.map = def('Right#map', [Function], function(f) {
     return Right(f(this.value));


Test patch:
diff --git a/test/index.js b/test/index.js
index 343566b..78722e1 100644
--- a/test/index.js
+++ b/test/index.js
@@ -656,6 +656,16 @@ describe('either', function() {
       // jshint +W053
     });
 
+    it('provides an "extend" method', function() {
+      eq(S.Left('abc').extend.length, 1);
+      eq(S.Left('abc').extend(function(x) { return x / 2; }), S.Left('abc'));
+
+      assert.throws(function() { S.Left('abc').extend([1, 2, 3]); },
+                    errorEq(TypeError,
+                            'Left#extend requires a value of type Function' +
+                            ' as its first argument; received [1, 2, 3]'));
+    });
+
     it('provides a "map" method', function() {
       eq(S.Left('abc').map.length, 1);
       eq(S.Left('abc').map(square), S.Left('abc'));
@@ -822,6 +832,16 @@ describe('either', function() {
       // jshint +W053
     });
 
+    it('provides an "extend" method', function() {
+      eq(S.Right(42).extend.length, 1);
+      eq(S.Right(42).extend(function(x) { return x / 2; }), S.Right(21));
+
+      assert.throws(function() { S.Right('abc').extend([1, 2, 3]); },
+                    errorEq(TypeError,
+                            'Right#extend requires a value of type Function' +
+                            ' as its first argument; received [1, 2, 3]'));
+    });
+
     it('provides a "map" method', function() {
       eq(S.Right(42).map.length, 1);
       eq(S.Right(42).map(square), S.Right(1764));