Repository: plaid/sanctuary
Pull request #3 (2015-02-02 04:20:26)

monad: remove S.bind and S.then in favour of R.chain compatibility
Is this a step in the right direction do you think, @buzzdecafe?


buzzdecafe: do you have any interest in fabtasy-land compat? then you would rename `bind` to chain and not worry about native `bind`
davidchambers: > do you have any interest in fantasy-land compat? then you would rename `bind` to chain and not worry about native `bind`

Ah, so *that's* what fantasy-land's `chain` does! Great idea, @buzzdecafe. The only question is whether the exported function should be named `S.bind` or `S.chain`. I prefer the former: Haskell's `>>=` is pronounced "bind", and "chain" only makes sense when used as a method (e.g. `m.chain(f).chain(g)`). It would mean a function named "bind" delegates to a method named "chain", but I *think* I'm okay with that.

benperez: :dromedary_camel: 
buzzdecafe: for the record, `ramda.chain` also dispatches to `chain` "methods"
davidchambers: > for the record, `ramda.chain` also dispatches to `chain` "methods"

In that case `S.bind` is an :alien:! Perhaps the best course of action is to remove `S.bind` and `S.then` and use `R.chain` instead. :)

buzzdecafe: :+1: 

Patch:
diff --git a/index.js b/index.js
index dfa8d63..64d32db 100644
--- a/index.js
+++ b/index.js
@@ -55,6 +55,11 @@
   }
   extend(Nothing, Maybe);
 
+  //  Nothing#chain :: (a -> m b) -> m b
+  Nothing.prototype.chain = function(f) {  // jshint ignore:line
+    return this;
+  };
+
   //  Nothing#equals :: Maybe a -> Boolean
   Nothing.prototype.equals = function(maybe) {
     return maybe instanceof Nothing;
@@ -79,6 +84,11 @@
   }
   extend(Just, Maybe);
 
+  //  Just#chain :: (a -> m b) -> m b
+  Just.prototype.chain = function(f) {
+    return f(this.value);
+  };
+
   //  Just#equals :: Maybe a -> Boolean
   Just.prototype.equals = function(maybe) {
     return maybe instanceof Just && maybe.value === this.value;
@@ -167,24 +177,6 @@
     }
   });
 
-  //  monad  /////////////////////////////////////////////////////////////////
-
-  //  bind :: m a -> (a -> m b) -> m b
-  var bind = curry(function(m, f) {
-    if (isArray(m)) {
-      return m.length > 0 ? f(m[0]) : m;
-    } else if (m.type === Maybe) {
-      return m instanceof Just ? f(m.value) : m;
-    } else {
-      throw new TypeError('Pattern match failure');
-    }
-  });
-
-  //  then :: (a -> m b) -> m a -> m b
-  var then = curry(function(f, m) {
-    return bind(m, f);
-  });
-
   //  control  ///////////////////////////////////////////////////////////////
 
   //  or :: f a -> f a -> f a
@@ -265,7 +257,6 @@
     Nothing: Nothing,
     Right: Right,
     at: at,
-    bind: bind,
     either: either,
     get: get,
     head: head,
@@ -277,7 +268,6 @@
     parseInt: parseInt_,
     parseJson: parseJson,
     tail: tail,
-    then: then,
     toMaybe: toMaybe,
   };
 


Test patch:
diff --git a/test/index.js b/test/index.js
index a36add3..a7cbc39 100644
--- a/test/index.js
+++ b/test/index.js
@@ -46,6 +46,12 @@ describe('maybe', function() {
       eq(nothing.type, S.Maybe);
     });
 
+    it('provides a "chain" method', function() {
+      var nothing = S.Nothing();
+      eq(nothing.chain.length, 1);
+      eq(nothing.chain(S.head), nothing);
+    });
+
     it('provides an "equals" method', function() {
       var nothing = S.Nothing();
       eq(nothing.equals.length, 1);
@@ -92,6 +98,12 @@ describe('maybe', function() {
       eq(just.type, S.Maybe);
     });
 
+    it('provides a "chain" method', function() {
+      var just = S.Just([1, 2, 3]);
+      eq(just.chain.length, 1);
+      assert(just.chain(S.head).equals(S.Just(1)));
+    });
+
     it('provides an "equals" method', function() {
       var just = S.Just(42);
       eq(just.equals.length, 1);
@@ -289,91 +301,6 @@ describe('either', function() {
 
 });
 
-describe('monad', function() {
-
-  describe('bind', function() {
-
-    //  toNumber :: String -> [Number]
-    var toNumber = function(s) {
-      var n = parseFloat(s);
-      return n === n ? [n] : [];
-    };
-
-    it('can be applied to empty array', function() {
-      var m = S.bind([], toNumber);
-      eq(Object.prototype.toString.call(m), '[object Array]');
-      eq(m.length, 0);
-    });
-
-    it('can be applied to singleton array', function() {
-      var m = S.bind(['42'], toNumber);
-      eq(Object.prototype.toString.call(m), '[object Array]');
-      eq(m.length, 1);
-      eq(m[0], 42);
-
-      var m2 = S.bind(['xxx'], toNumber);
-      eq(Object.prototype.toString.call(m2), '[object Array]');
-      eq(m2.length, 0);
-    });
-
-    it('can be applied to a Nothing', function() {
-      var m = S.bind(S.Nothing(), S.head);
-      assert(m.equals(S.Nothing()));
-    });
-
-    it('can be applied to a Just', function() {
-      var m = S.bind(S.Just([42, 21]), S.head);
-      assert(m.equals(S.Just(42)));
-    });
-
-    it('throws if applied to a non-monad', function() {
-      assert.throws(
-        function() { S.bind(/xxx/, S.head); },
-        function(err) {
-          return err instanceof TypeError &&
-                 err.message === 'Pattern match failure';
-        }
-      );
-    });
-
-    it('is curried', function() {
-      var maybe = S.bind(S.Just([42, 21]))(S.head);
-      assert(maybe.equals(S.Just(42)));
-    });
-
-  });
-
-  describe('then', function() {
-
-    it('can be applied to a Nothing', function() {
-      var maybe = S.then(S.head, S.Nothing());
-      assert(maybe.equals(S.Nothing()));
-    });
-
-    it('can be applied to a Just', function() {
-      var maybe = S.then(S.head, S.Just([42, 21]));
-      assert(maybe.equals(S.Just(42)));
-    });
-
-    it('throws if applied to a value of non-monad', function() {
-      assert.throws(
-        function() { S.then(S.head, /xxx/); },
-        function(err) {
-          return err instanceof TypeError &&
-                 err.message === 'Pattern match failure';
-        }
-      );
-    });
-
-    it('is curried', function() {
-      var maybe = S.then(S.head)(S.Just([42, 21]));
-      assert(maybe.equals(S.Just(42)));
-    });
-
-  });
-
-});
-
 describe('control', function() {
 
   describe('or', function() {