Repository: plaid/sanctuary
Pull request #69 (2015-08-13 03:20:59)

Adds Maybe#reduce.
Not sure what I'm doing wrong, but despite the tests passing when run manually, running `make test` reports:
```
sanctuary/index.js:110
    return x != null && Object(x) instanceof type;
                                             ^
TypeError: Expecting a function in instanceof check, but got undefined
    at _is (sanctuary/index.js:110:46)
    at Just.<anonymous> (sanctuary/index.js:164:21)
    at Just.map (sanctuary/index.js:116:45)
    at [stdin]:63:25
    at Object.exports.runInThisContext (vm.js:74:17)
    at Object.<anonymous> ([stdin]-wrapper:6:22)
    at Module._compile (module.js:460:26)
    at evalScript (node.js:431:25)
    at Socket.<anonymous> (node.js:164:11)
    at Socket.emit (events.js:129:20)
```

davidchambers: Thanks for the pull request, @scott-christopher! One of the doctests is incorrect, which is causing the test failure. I'm sorry that the error output is not helpful. If you look in the makefile you'll see a hacky Unix pipeline. I plan to integrate [doctest][1] at some point, which will give us descriptive error messages.


[1]: https://github.com/davidchambers/doctest

davidchambers: Could you update this section to include Foldable?

> ```javascript
> //. The Maybe type satisfies the [Monoid][], [Monad][], and [Extend][]
> //. specifications.
> ```

scott-christopher: I've pushed an update based on your feedback.

Patch:
diff --git a/index.js b/index.js
index 520c67c..64d6b0b 100644
--- a/index.js
+++ b/index.js
@@ -258,8 +258,8 @@
   //. The Maybe type represents optional values: a value of type `Maybe a` is
   //. either a Just whose value is of type `a` or a Nothing (with no value).
   //.
-  //. The Maybe type satisfies the [Monoid][], [Monad][], and [Extend][]
-  //. specifications.
+  //. The Maybe type satisfies the [Monoid][], [Monad][], [Foldable][], and
+  //. [Extend][] specifications.
   var Maybe = S.Maybe = function Maybe() {
     throw new Error('Cannot instantiate Maybe');
   };
@@ -441,6 +441,21 @@
   //. ```
   Maybe.prototype.of = def('Maybe#of', [a], Maybe.of);
 
+  //# Maybe#reduce :: Maybe a ~> (b -> a -> b) -> b -> b
+  //.
+  //. Takes a function and an initial value, returning:
+  //. * the initial value if `this` is a Nothing; otherwise
+  //. * the result of applying the function to the initial value and the value
+  //.   of this Just.
+  //.
+  //. ```javascript
+  //. > S.Nothing().reduce(R.add, 10)
+  //. 10
+  //.
+  //. > S.Just(5).reduce(R.add, 10)
+  //. 15
+  //. ```
+
   //# Maybe#toBoolean :: Maybe a ~> Boolean
   //.
   //. Returns `false` if `this` is a Nothing; `true` if `this` is a Just.
@@ -505,6 +520,10 @@
   //  Nothing#map :: Maybe a ~> (a -> b) -> Maybe b
   Nothing.prototype.map = def('Nothing#map', [Function], self);
 
+  //  Nothing#reduce :: Maybe a ~> (b -> a -> b) -> b -> b
+  Nothing.prototype.reduce =
+    def('Nothing#reduce', [Function, a], function(f, z) { return z; });
+
   //  Nothing#toBoolean :: Maybe a ~> Boolean
   Nothing.prototype.toBoolean = def('Nothing#toBoolean', [], R.always(false));
 
@@ -561,6 +580,11 @@
     return Just(f(this.value));
   });
 
+  //  Just#reduce :: Maybe a ~> (b -> a -> b) -> b -> b
+  Just.prototype.reduce = def('Just#reduce', [Function, a], function(f, z) {
+    return f(z, this.value);
+  });
+
   //  Just#toBoolean :: Maybe a ~> Boolean
   Just.prototype.toBoolean = def('Just#toBoolean', [], R.always(true));
 


Test patch:
diff --git a/test/index.js b/test/index.js
index 5df8300..f2a3cad 100644
--- a/test/index.js
+++ b/test/index.js
@@ -271,6 +271,16 @@ describe('maybe', function() {
                             'Function as its first argument; received null'));
     });
 
+    it('provides a "reduce" method', function() {
+      eq(S.Nothing().reduce.length, 2);
+      eq(S.Nothing().reduce(function(a, b) { return a + b; }, 10), 10);
+
+      assert.throws(function() { S.Nothing().reduce(null); },
+                    errorEq(TypeError,
+                            '‘Nothing#reduce’ requires a value of type ' +
+                            'Function as its first argument; received null'));
+    });
+
     it('provides a "toBoolean" method', function() {
       eq(S.Nothing().toBoolean.length, 0);
       eq(S.Nothing().toBoolean(), false);
@@ -487,6 +497,16 @@ describe('maybe', function() {
                             'as its first argument; received [1, 2, 3]'));
     });
 
+    it('provides a "reduce" method', function() {
+      eq(S.Just(5).reduce.length, 2);
+      eq(S.Just(5).reduce(function(a, b) { return a + b; }, 10), 15);
+
+      assert.throws(function() { S.Just().reduce(null); },
+                    errorEq(TypeError,
+                            '‘Just#reduce’ requires a value of type ' +
+                            'Function as its first argument; received null'));
+    });
+
     it('provides a "toBoolean" method', function() {
       eq(S.Just(42).toBoolean.length, 0);
       eq(S.Just(42).toBoolean(), true);