Repository: sanctuary-js/sanctuary
Pull request #323 (2017-01-12 10:06:12)

maybe: define "fantasy-land/zero"
Could you check the laws, @safareli?



Patch:
diff --git a/index.js b/index.js
index e3b0f41..05fd66d 100644
--- a/index.js
+++ b/index.js
@@ -862,7 +862,7 @@
   //. either a Just whose value is of type `a` or Nothing (with no value).
   //.
   //. The Maybe type satisfies the [Setoid][], [Monoid][], [Monad][],
-  //. [Traversable][], and [Extend][] specifications.
+  //. [Alternative][], [Traversable][], and [Extend][] specifications.
 
   //# MaybeType :: Type -> Type
   //.
@@ -930,6 +930,16 @@
   //. ```
   Maybe['fantasy-land/of'] = Just;
 
+  //# Maybe.fantasy-land/zero :: () -> Maybe a
+  //.
+  //. Returns Nothing.
+  //.
+  //. ```javascript
+  //. > Z.zero(S.Maybe)
+  //. Nothing
+  //. ```
+  Maybe['fantasy-land/zero'] = function() { return Nothing; };
+
   //# Maybe#isNothing :: Maybe a ~> Boolean
   //.
   //. `true` if `this` is Nothing; `false` if `this` is a Just.
@@ -1477,7 +1487,7 @@
   //. value is of type `b`.
   //.
   //. The Either type satisfies the [Setoid][], [Semigroup][], [Monad][],
-  //. [Traversable][], [Extend][], and [Bifunctor][] specifications.
+  //. [Alt][], [Traversable][], [Extend][], and [Bifunctor][] specifications.
 
   //# EitherType :: Type -> Type -> Type
   //.
@@ -3511,6 +3521,8 @@
 
 //. [$.Array]:          v:sanctuary-js/sanctuary-def#Array
 //. [$.String]:         v:sanctuary-js/sanctuary-def#String
+//. [Alt]:              v:fantasyland/fantasy-land#alt
+//. [Alternative]:      v:fantasyland/fantasy-land#alternative
 //. [Applicative]:      v:fantasyland/fantasy-land#applicative
 //. [Apply]:            v:fantasyland/fantasy-land#apply
 //. [Bifunctor]:        v:fantasyland/fantasy-land#bifunctor


Test patch:
diff --git a/test/Either/Either.js b/test/Either/Either.js
index ebaa69a..52030d3 100644
--- a/test/Either/Either.js
+++ b/test/Either/Either.js
@@ -140,6 +140,24 @@ suite('Either', function() {
 
   });
 
+  suite('Alt laws', function() {
+
+    var altLaws = laws.Alt(Z.equals);
+
+    altLaws.associativity(
+      EitherArb(jsc.string, jsc.number),
+      EitherArb(jsc.string, jsc.number),
+      EitherArb(jsc.string, jsc.number)
+    );
+
+    altLaws.distributivity(
+      EitherArb(jsc.string, jsc.number),
+      EitherArb(jsc.string, jsc.number),
+      jsc.constant(Math.sqrt)
+    );
+
+  });
+
   suite('Foldable laws', function() {
 
     var foldableLaws = laws.Foldable(Z.equals);
diff --git a/test/Maybe/Maybe.js b/test/Maybe/Maybe.js
index 346cdf9..c83fac4 100644
--- a/test/Maybe/Maybe.js
+++ b/test/Maybe/Maybe.js
@@ -136,6 +136,58 @@ suite('Maybe', function() {
 
   });
 
+  suite('Alt laws', function() {
+
+    var altLaws = laws.Alt(Z.equals);
+
+    altLaws.associativity(
+      MaybeArb(jsc.number),
+      MaybeArb(jsc.number),
+      MaybeArb(jsc.number)
+    );
+
+    altLaws.distributivity(
+      MaybeArb(jsc.number),
+      MaybeArb(jsc.number),
+      jsc.constant(Math.sqrt)
+    );
+
+  });
+
+  suite('Plus laws', function() {
+
+    var plusLaws = laws.Plus(Z.equals, S.Maybe);
+
+    plusLaws.leftIdentity(
+      MaybeArb(jsc.number)
+    );
+
+    plusLaws.rightIdentity(
+      MaybeArb(jsc.number)
+    );
+
+    plusLaws.annihilation(
+      jsc.constant(Math.sqrt)
+    );
+
+  });
+
+  suite('Alternative laws', function() {
+
+    var alternativeLaws = laws.Alternative(Z.equals, S.Maybe);
+
+    alternativeLaws.distributivity(
+      MaybeArb(jsc.number),
+      MaybeArb(jsc.constant(Math.sqrt)),
+      MaybeArb(jsc.constant(Math.abs))
+    );
+
+    alternativeLaws.annihilation(
+      MaybeArb(jsc.number)
+    );
+
+  });
+
   suite('Foldable laws', function() {
 
     var foldableLaws = laws.Foldable(Z.equals);
diff --git a/test/internal/laws/Alt.js b/test/internal/laws/Alt.js
new file mode 100644
index 0000000..f84ef30
--- /dev/null
+++ b/test/internal/laws/Alt.js
@@ -0,0 +1,26 @@
+'use strict';
+
+var alt = require('./alt_');
+var forall = require('./forall');
+var map = require('./map');
+
+
+module.exports = function(equals) {
+  return forall({
+
+    //  (a <|> b) <|> c = a <|> (b <|> c)
+    associativity: function(a, b, c) {
+      var lhs = alt(alt(a)(b))(c);
+      var rhs = alt(a)(alt(b)(c));
+      return equals(lhs, rhs);
+    },
+
+    //  f <$> (a <|> b) = (f <$> a) <|> (f <$> b)
+    distributivity: function(a, b, f) {
+      var lhs = map(f)(alt(a)(b));
+      var rhs = alt(map(f)(a))(map(f)(b));
+      return equals(lhs, rhs);
+    }
+
+  });
+};
diff --git a/test/internal/laws/Alternative.js b/test/internal/laws/Alternative.js
new file mode 100644
index 0000000..c60a21c
--- /dev/null
+++ b/test/internal/laws/Alternative.js
@@ -0,0 +1,29 @@
+'use strict';
+
+var Z = require('sanctuary-type-classes');
+
+var alt = require('./alt_');
+var ap = require('./ap');
+var forall = require('./forall');
+
+
+module.exports = function(equals, A) {
+  var zero = Z.zero(A);
+  return forall({
+
+    //  (f <|> g) <*> x = (f <*> x) <|> (g <*> x)
+    distributivity: function(x, f, g) {
+      var lhs = ap(alt(f)(g))(x);
+      var rhs = alt(ap(f)(x))(ap(g)(x));
+      return equals(lhs, rhs);
+    },
+
+    //  zero <*> x = zero
+    annihilation: function(x) {
+      var lhs = ap(zero)(x);
+      var rhs = zero;
+      return equals(lhs, rhs);
+    }
+
+  });
+};
diff --git a/test/internal/laws/Monoid.js b/test/internal/laws/Monoid.js
index f346070..a89ffd2 100644
--- a/test/internal/laws/Monoid.js
+++ b/test/internal/laws/Monoid.js
@@ -10,14 +10,14 @@ module.exports = function(equals, M) {
   var empty = Z.empty(M);
   return forall({
 
-    //  (empty m) `concat` m = m
+    //  empty `concat` m = m
     leftIdentity: function(m) {
       var lhs = concat(empty)(m);
       var rhs = m;
       return equals(lhs, rhs);
     },
 
-    //  m `concat` (empty m) = m
+    //  m `concat` empty = m
     rightIdentity: function(m) {
       var lhs = concat(m)(empty);
       var rhs = m;
diff --git a/test/internal/laws/Plus.js b/test/internal/laws/Plus.js
new file mode 100644
index 0000000..f7142a4
--- /dev/null
+++ b/test/internal/laws/Plus.js
@@ -0,0 +1,36 @@
+'use strict';
+
+var Z = require('sanctuary-type-classes');
+
+var alt = require('./alt_');
+var forall = require('./forall');
+var map = require('./map');
+
+
+module.exports = function(equals, P) {
+  var zero = Z.zero(P);
+  return forall({
+
+    //  zero <|> x = x
+    leftIdentity: function(x) {
+      var lhs = alt(zero)(x);
+      var rhs = x;
+      return equals(lhs, rhs);
+    },
+
+    //  x <|> zero = x
+    rightIdentity: function(x) {
+      var lhs = alt(x)(zero);
+      var rhs = x;
+      return equals(lhs, rhs);
+    },
+
+    //  f <$> zero = zero
+    annihilation: function(f) {
+      var lhs = map(f)(zero);
+      var rhs = zero;
+      return equals(lhs, rhs);
+    }
+
+  });
+};
diff --git a/test/internal/laws/alt_.js b/test/internal/laws/alt_.js
new file mode 100644
index 0000000..183ad1d
--- /dev/null
+++ b/test/internal/laws/alt_.js
@@ -0,0 +1,8 @@
+'use strict';
+
+var Z = require('sanctuary-type-classes');
+
+var curry2 = require('./curry2');
+
+//  alt :: Alt f => f a -> f a -> f a
+module.exports = curry2(Z.alt);
diff --git a/test/internal/laws/index.js b/test/internal/laws/index.js
index f1979e4..6302f5e 100644
--- a/test/internal/laws/index.js
+++ b/test/internal/laws/index.js
@@ -1,6 +1,8 @@
 'use strict';
 
 module.exports = {
+  Alt: require('./Alt'),
+  Alternative: require('./Alternative'),
   Applicative: require('./Applicative'),
   Apply: require('./Apply'),
   Bifunctor: require('./Bifunctor'),
@@ -10,6 +12,7 @@ module.exports = {
   Functor: require('./Functor'),
   Monad: require('./Monad'),
   Monoid: require('./Monoid'),
+  Plus: require('./Plus'),
   Semigroup: require('./Semigroup'),
   Setoid: require('./Setoid'),
   Traversable: require('./Traversable')