Repository: sanctuary-js/sanctuary (js)

Issue:
eitherToMaybe :: Either a b -> Maybe b
@kedashoe added [`maybeToEither`][1] in #76. It would be nice to provide a function for going in the other direction. The implementation is not complex, and already appears in the [test suite][2]:

> ```javascript
> //  eitherToMaybe :: Either a b -> Maybe b
> var eitherToMaybe = S.either(S.K(S.Nothing()), S.Just);
> ```


[1]: http://sanctuary.js.org/#maybeToEither
[2]: https://github.com/sanctuary-js/sanctuary/blob/v0.10.0/test/index.js#L117-L118


Pull request description:
either: add S.eitherToMaybe
Closes #203


Patch:
diff --git a/index.js b/index.js
index 930caf8..b0dcde5 100644
--- a/index.js
+++ b/index.js
@@ -1302,7 +1302,7 @@
   //. Binary version of [`encase`](#encase).
   //.
   //. See also [`encase2_`](#encase2_).
-  S.encase2 =
+  var encase2 = S.encase2 =
   def('encase2',
       {},
       [$.Function, a, b, $Maybe(c)],
@@ -1327,7 +1327,7 @@
             return f_(x, y);
           };
         };
-        return S.encase2(f, x, y);
+        return encase2(f, x, y);
       });
 
   //# encase3 :: (a -> b -> c -> d) -> a -> b -> c -> Maybe d
@@ -1335,7 +1335,7 @@
   //. Ternary version of [`encase`](#encase).
   //.
   //. See also [`encase3_`](#encase3_).
-  S.encase3 =
+  var encase3 = S.encase3 =
   def('encase3',
       {},
       [$.Function, a, b, c, $Maybe(d)],
@@ -1362,7 +1362,29 @@
             };
           };
         };
-        return S.encase3(f, x, y, z);
+        return encase3(f, x, y, z);
+      });
+
+  //# maybeToEither :: a -> Maybe b -> Either a b
+  //.
+  //. Converts a Maybe to an Either. A Nothing becomes a Left (containing the
+  //. first argument); a Just becomes a Right.
+  //.
+  //. See also [`eitherToMaybe`](#eitherToMaybe).
+  //.
+  //. ```javascript
+  //. > S.maybeToEither('Expecting an integer', S.parseInt(10, 'xyz'))
+  //. Left('Expecting an integer')
+  //.
+  //. > S.maybeToEither('Expecting an integer', S.parseInt(10, '42'))
+  //. Right(42)
+  //. ```
+  S.maybeToEither =
+  def('maybeToEither',
+      {},
+      [a, $Maybe(b), $Either(a, b)],
+      function(x, maybe) {
+        return maybe.isNothing ? Left(x) : Right(maybe.value);
       });
 
   //. ### Either type
@@ -1876,7 +1898,7 @@
   //. Binary version of [`encaseEither`](#encaseEither).
   //.
   //. See also [`encaseEither2_`](#encaseEither2_).
-  S.encaseEither2 =
+  var encaseEither2 = S.encaseEither2 =
   def('encaseEither2',
       {},
       [$.Function, $.Function, a, b, $Either(l, r)],
@@ -1902,7 +1924,7 @@
             return g_(x, y);
           };
         };
-        return S.encaseEither2(f, g, x, y);
+        return encaseEither2(f, g, x, y);
       });
 
   //# encaseEither3 :: (Error -> l) -> (a -> b -> c -> r) -> a -> b -> c -> Either l r
@@ -1910,7 +1932,7 @@
   //. Ternary version of [`encaseEither`](#encaseEither).
   //.
   //. See also [`encaseEither3_`](#encaseEither3_).
-  S.encaseEither3 =
+  var encaseEither3 = S.encaseEither3 =
   def('encaseEither3',
       {},
       [$.Function, $.Function, a, b, c, $Either(l, r)],
@@ -1938,29 +1960,29 @@
             };
           };
         };
-        return S.encaseEither3(f, g, x, y, z);
+        return encaseEither3(f, g, x, y, z);
       });
 
-  //# maybeToEither :: a -> Maybe b -> Either a b
+  //# eitherToMaybe :: Either a b -> Maybe b
   //.
-  //. Takes a value of any type and a Maybe, and returns an Either.
-  //. If the second argument is a Nothing, a Left containing the first
-  //. argument is returned. If the second argument is a Just, a Right
-  //. containing the Just's value is returned.
+  //. Converts an Either to a Maybe. A Left becomes a Nothing; a Right becomes
+  //. a Just.
+  //.
+  //. See also [`maybeToEither`](#maybeToEither).
   //.
   //. ```javascript
-  //. > S.maybeToEither('Expecting an integer', S.parseInt(10, 'xyz'))
-  //. Left('Expecting an integer')
+  //. > S.eitherToMaybe(S.Left('Cannot divide by zero'))
+  //. Nothing()
   //.
-  //. > S.maybeToEither('Expecting an integer', S.parseInt(10, '42'))
-  //. Right(42)
+  //. > S.eitherToMaybe(S.Right(42))
+  //. Just(42)
   //. ```
-  S.maybeToEither =
-  def('maybeToEither',
+  S.eitherToMaybe =
+  def('eitherToMaybe',
       {},
-      [a, $Maybe(b), $Either(a, b)],
-      function(x, maybe) {
-        return maybe.isNothing ? Left(x) : Right(maybe.value);
+      [$Either(a, b), $Maybe(b)],
+      function(either) {
+        return either.isLeft ? Nothing() : Just(either.value);
       });
 
   //. ### Alternative


Test patch:
diff --git a/test/Maybe/Maybe.js b/test/Maybe/Maybe.js
index d7d03ce..7f82157 100644
--- a/test/Maybe/Maybe.js
+++ b/test/Maybe/Maybe.js
@@ -43,9 +43,6 @@ var JustArb = function(arb) {
   return arb.smap(S.Just, function(m) { return m.value; }, R.toString);
 };
 
-//  eitherToMaybe :: Either a b -> Maybe b
-var eitherToMaybe = S.either(S.K(S.Nothing()), S.Just);
-
 //  EitherArb :: Arbitrary a -> Arbitrary b -> Arbitrary (Either a b)
 var EitherArb = function(lArb, rArb) {
   return jsc.oneof(LeftArb(lArb), RightArb(rArb));
@@ -97,8 +94,8 @@ describe('Maybe', function() {
 
     it('satisfies naturality', function() {
       jsc.assert(jsc.forall(MaybeArb(EitherArb(jsc.integer, jsc.string)), function(maybe) {
-        var lhs = eitherToMaybe(maybe.sequence(S.Either.of));
-        var rhs = maybe.map(eitherToMaybe).sequence(S.Maybe.of);
+        var lhs = S.eitherToMaybe(maybe.sequence(S.Either.of));
+        var rhs = maybe.map(S.eitherToMaybe).sequence(S.Maybe.of);
         return lhs.equals(rhs);
       }));
     });
diff --git a/test/eitherToMaybe.js b/test/eitherToMaybe.js
new file mode 100644
index 0000000..c97e082
--- /dev/null
+++ b/test/eitherToMaybe.js
@@ -0,0 +1,39 @@
+'use strict';
+
+var throws = require('assert').throws;
+
+var eq = require('./utils').eq;
+var errorEq = require('./utils').errorEq;
+var S = require('..');
+
+
+describe('eitherToMaybe', function() {
+
+  it('is a unary function', function() {
+    eq(typeof S.eitherToMaybe, 'function');
+    eq(S.eitherToMaybe.length, 1);
+  });
+
+  it('type checks its arguments', function() {
+    throws(function() { S.eitherToMaybe(/XXX/); },
+           errorEq(TypeError,
+                   'Invalid value\n' +
+                   '\n' +
+                   'eitherToMaybe :: Either a b -> Maybe b\n' +
+                   '                 ^^^^^^^^^^\n' +
+                   '                     1\n' +
+                   '\n' +
+                   '1)  /XXX/ :: RegExp\n' +
+                   '\n' +
+                   'The value at position 1 is not a member of ‘Either a b’.\n'));
+  });
+
+  it('returns a Nothing when applied to a Left', function() {
+    eq(S.eitherToMaybe(S.Left('Cannot divide by zero')), S.Nothing());
+  });
+
+  it('returns a Just when applied to a Right', function() {
+    eq(S.eitherToMaybe(S.Right(42)), S.Just(42));
+  });
+
+});
diff --git a/test/match.js b/test/match.js
index e01a8c9..e4daa7e 100644
--- a/test/match.js
+++ b/test/match.js
@@ -56,7 +56,7 @@ describe('match', function() {
        S.Just([S.Just('bye'), S.Nothing()]));
   });
 
-  it('returns a Nothing() if no match', function() {
+  it('returns a Nothing if no match', function() {
     eq(S.match(/zzz/, 'abcdefg'), S.Nothing());
   });