Repository: sanctuary-js/sanctuary (js)

Issue:
fromMaybe_ :: (() -> a) -> Maybe a -> a
We added `maybe_`, a "lazy" version of [`maybe`][1], in #285. `maybe` is a convenient function for situations in which one wants to `map` then apply [`fromMaybe`][2]. It seems to me we should also provide a "lazy" version of `fromMaybe`. Otherwise we'll end up writing `S.maybe_(thunk, S.I, m)` in some cases, which seems a bit strange.

Do you agree?


[1]: https://sanctuary.js.org/#maybe
[2]: https://sanctuary.js.org/#fromMaybe


Pull request:
maybe: add S.fromMaybe_
Closes #296


Patch:
diff --git a/index.js b/index.js
index 97d792a..496bd76 100644
--- a/index.js
+++ b/index.js
@@ -1296,7 +1296,8 @@
   //. Takes a default value and a Maybe, and returns the Maybe's value
   //. if the Maybe is a Just; the default value otherwise.
   //.
-  //. See also [`maybeToNullable`](#maybeToNullable).
+  //. See also [`fromMaybe_`](#fromMaybe_) and
+  //. [`maybeToNullable`](#maybeToNullable).
   //.
   //. ```javascript
   //. > S.fromMaybe(0, S.Just(42))
@@ -1310,6 +1311,25 @@
   }
   S.fromMaybe = def('fromMaybe', {}, [a, $Maybe(a), a], fromMaybe);
 
+  //# fromMaybe_ :: (() -> a) -> Maybe a -> a
+  //.
+  //. Variant of [`fromMaybe`](#fromMaybe) which takes a thunk so the default
+  //. value is only computed if required.
+  //.
+  //. ```javascript
+  //. > function fib(n) { return n <= 1 ? n : fib(n - 2) + fib(n - 1); }
+  //.
+  //. > S.fromMaybe_(() => fib(30), S.Just(1000000))
+  //. 1000000
+  //.
+  //. > S.fromMaybe_(() => fib(30), S.Nothing)
+  //. 832040
+  //. ```
+  function fromMaybe_(thunk, maybe) {
+    return maybe.isJust ? maybe.value : thunk();
+  }
+  S.fromMaybe_ = def('fromMaybe_', {}, [$.Function, $Maybe(a), a], fromMaybe_);
+
   //# maybeToNullable :: Maybe a -> Nullable a
   //.
   //. Returns the given Maybe's value if the Maybe is a Just; `null` otherwise.
@@ -1373,10 +1393,7 @@
   //. is only computed if required.
   //.
   //. ```javascript
-  //. > global.fib = function fib(n) {
-  //. .   return n <= 1 ? n : fib(n - 2) + fib(n - 1);
-  //. . }
-  //. fib
+  //. > function fib(n) { return n <= 1 ? n : fib(n - 2) + fib(n - 1); }
   //.
   //. > S.maybe_(() => fib(30), Math.sqrt, S.Just(1000000))
   //. 1000
@@ -1384,8 +1401,8 @@
   //. > S.maybe_(() => fib(30), Math.sqrt, S.Nothing)
   //. 832040
   //. ```
-  function maybe_(f, g, maybe) {
-    return maybe.isJust ? g(maybe.value) : f();
+  function maybe_(thunk, f, maybe) {
+    return maybe.isJust ? f(maybe.value) : thunk();
   }
   S.maybe_ = def('maybe_', {}, [$.Function, $.Function, $Maybe(a), b], maybe_);
 
diff --git a/package.json b/package.json
index 8ca89dd..b19077f 100644
--- a/package.json
+++ b/package.json
@@ -16,7 +16,7 @@
   },
   "devDependencies": {
     "browserify": "13.1.x",
-    "doctest": "0.10.x",
+    "doctest": "0.11.x",
     "eslint": "2.9.x",
     "fantasy-land": "0.3.0",
     "istanbul": "0.4.x",


Test patch:
diff --git a/test/fromMaybe_.js b/test/fromMaybe_.js
new file mode 100644
index 0000000..1483c3c
--- /dev/null
+++ b/test/fromMaybe_.js
@@ -0,0 +1,47 @@
+'use strict';
+
+var R = require('ramda');
+
+var S = require('..');
+
+var eq = require('./internal/eq');
+var throws = require('./internal/throws');
+
+
+test('fromMaybe_', function() {
+
+  eq(typeof S.fromMaybe_, 'function');
+  eq(S.fromMaybe_.length, 2);
+
+  throws(function() { S.fromMaybe_(0); },
+         TypeError,
+         'Invalid value\n' +
+         '\n' +
+         'fromMaybe_ :: Function -> Maybe a -> a\n' +
+         '              ^^^^^^^^\n' +
+         '                 1\n' +
+         '\n' +
+         '1)  0 :: Number, FiniteNumber, Integer, ValidNumber\n' +
+         '\n' +
+         'The value at position 1 is not a member of ‘Function’.\n');
+
+  throws(function() { S.fromMaybe_(R.always(0), [1, 2, 3]); },
+         TypeError,
+         'Invalid value\n' +
+         '\n' +
+         'fromMaybe_ :: Function -> Maybe a -> a\n' +
+         '                          ^^^^^^^\n' +
+         '                             1\n' +
+         '\n' +
+         '1)  [1, 2, 3] :: Array Number, Array FiniteNumber, Array NonZeroFiniteNumber, Array Integer, Array ValidNumber\n' +
+         '\n' +
+         'The value at position 1 is not a member of ‘Maybe a’.\n');
+
+  eq(S.fromMaybe_(R.always(0), S.Nothing), 0);
+  eq(S.fromMaybe_(R.always(0), S.Just(42)), 42);
+
+  var count = 0;
+  eq(S.fromMaybe_(function() { return count += 1; }, S.Just(42)), 42);
+  eq(count, 0);
+
+});