Repository: plaid/sanctuary
Pull request #134 (2016-01-28 03:04:36)

string: add S.toUpper and S.toLower
```haskell
toUpper :: String -> String
toLower :: String -> String
```

This shouldn't be controversial. It's [`R.toUpper`][1] and [`R.toLower`][2], but with type checking. :)


[1]: http://ramdajs.com/docs/#toUpper
[2]: http://ramdajs.com/docs/#toLower



Patch:
diff --git a/index.js b/index.js
index 302880c..1a8505a 100644
--- a/index.js
+++ b/index.js
@@ -541,8 +541,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][], [Traversable][],
-  //. and [Extend][] specifications.
+  //. The Maybe type satisfies the [Monoid][], [Monad][], [Foldable][], and
+  //. [Extend][] specifications.
   var Maybe = S.Maybe = function Maybe() {
     if (arguments[0] !== sentinel) {
       throw new Error('Cannot instantiate Maybe');
@@ -832,29 +832,6 @@
            return maybe.isJust ? f(x, maybe.value) : x;
          });
 
-  //# Maybe#sequence :: Applicative f => Maybe (f a) ~> (a -> f a) -> f (Maybe a)
-  //.
-  //. Evaluates an applicative action contained within the Maybe, resulting in:
-  //.
-  //.   - a pure applicative of a Nothing if `this` is a Nothing; otherwise
-  //.
-  //.   - an applicative of Just the value of the evaluated action.
-  //.
-  //. ```javascript
-  //. > S.Nothing().sequence(S.Either.of)
-  //. Right(Nothing())
-  //.
-  //. > S.Just(Right(42)).sequence(S.Either.of)
-  //. Right(Just(42))
-  //. ```
-  Maybe.prototype.sequence =
-  method('Maybe#sequence',
-         {a: [Apply], b: [Apply]},
-         [$Maybe(a), $.Function, b],
-         function(maybe, of) {
-           return maybe.isJust ? R.map(Just, maybe.value) : of(maybe);
-         });
-
   //# Maybe#toBoolean :: Maybe a ~> Boolean
   //.
   //. Returns `false` if `this` is a Nothing; `true` if `this` is a Just.
@@ -2470,6 +2447,38 @@
 
   //. ### String
 
+  //# toUpper :: String -> String
+  //.
+  //. Returns the upper-case equivalent of its argument.
+  //.
+  //. See also [`toLower`](#toLower).
+  //.
+  //. ```javascript
+  //. > S.toUpper('ABC def 123')
+  //. 'ABC DEF 123'
+  //. ```
+  S.toUpper =
+  def('toUpper',
+      {},
+      [$.String, $.String],
+      function(s) { return s.toUpperCase(); });
+
+  //# toLower :: String -> String
+  //.
+  //. Returns the lower-case equivalent of its argument.
+  //.
+  //. See also [`toUpper`](#toUpper).
+  //.
+  //. ```javascript
+  //. > S.toLower('ABC def 123')
+  //. 'abc def 123'
+  //. ```
+  S.toLower =
+  def('toLower',
+      {},
+      [$.String, $.String],
+      function(s) { return s.toLowerCase(); });
+
   //# words :: String -> [String]
   //.
   //. Takes a string and returns the list of words the string contains
@@ -2545,10 +2554,10 @@
 
 //. [Apply]:        https://github.com/fantasyland/fantasy-land#apply
 //. [Extend]:       https://github.com/fantasyland/fantasy-land#extend
+//. [Foldable]:     https://github.com/fantasyland/fantasy-land#foldable
 //. [Functor]:      https://github.com/fantasyland/fantasy-land#functor
 //. [Monad]:        https://github.com/fantasyland/fantasy-land#monad
 //. [Monoid]:       https://github.com/fantasyland/fantasy-land#monoid
-//. [Traversable]:  https://github.com/fantasyland/fantasy-land#traversable
 //. [R.equals]:     http://ramdajs.com/docs/#equals
 //. [R.map]:        http://ramdajs.com/docs/#map
 //. [R.type]:       http://ramdajs.com/docs/#type


Test patch:
diff --git a/test/index.js b/test/index.js
index a0137e3..4895f38 100644
--- a/test/index.js
+++ b/test/index.js
@@ -67,79 +67,6 @@ var squareRoot = function(n) {
                : S.Right(Math.sqrt(n));
 };
 
-//  Identity :: a -> Identity a
-var Identity = function(x) {
-  return {
-    map: function(fn) {
-      return Identity(fn(x));
-    },
-    ap: function(y) {
-      return Identity(x(y));
-    },
-    equals: function(other) {
-      return R.equals(x, other.value);
-    },
-    value: x
-  };
-};
-Identity.of = Identity;
-
-//  Compose :: Apply f, Apply g
-//          => { of: b -> f b } -> { of: c -> g c }
-//          -> f (g a) -> Compose f g a
-var Compose = function(F, G) {
-  function _Compose(x) {
-    return {
-      map: function(f) {
-        return _Compose(R.map(R.map(f), x));
-      },
-      ap: function(y) {
-        return _Compose(R.ap(R.map(R.ap, x), y.value));
-      },
-      equals: function(other) {
-        return R.equals(x, other.value);
-      },
-      value: x
-    };
-  }
-  _Compose.of = function(x) {
-    return _Compose(F.of(G.of(x)));
-  };
-  return _Compose;
-};
-
-//  eitherToMaybe :: Either a b -> Maybe b
-var eitherToMaybe = S.either(S.K(S.Nothing()), S.Just);
-
-//  JustArb :: Arbitrary a -> Arbitrary (Maybe a)
-var JustArb = function(arb) {
-  return arb.smap(S.Just, function(m) { return m.value; }, R.toString);
-};
-
-//  MaybeArb :: Arbitrary a -> Arbitrary (Maybe a)
-var MaybeArb = function(arb) {
-  return jsc.oneof(JustArb(arb), jsc.constant(S.Nothing()));
-};
-
-//  LeftArb :: Arbitrary a -> Arbitrary (Either a b)
-var LeftArb = function(arb) {
-  return arb.smap(S.Left, function(e) { return e.value; }, R.toString);
-};
-
-//  RightArb :: Arbitrary a -> Arbitrary (Either b a)
-var RightArb = function(arb) {
-  return arb.smap(S.Right, function(e) { return e.value; }, R.toString);
-};
-
-//  EitherArb :: Arbitrary a -> Arbitrary b -> Arbitrary (Either a b)
-var EitherArb = function(lArb, rArb) {
-  return jsc.oneof(LeftArb(lArb), RightArb(rArb));
-};
-
-//  IdentityArb :: Arbitrary a -> Arbitrary (Identity a)
-var IdentityArb = function(arb) {
-  return arb.smap(Identity, function(i) { return i.value; });
-};
 
 describe('invariants', function() {
 
@@ -551,37 +478,6 @@ describe('maybe', function() {
                     errorEq(Error, 'Cannot instantiate Maybe'));
     });
 
-    describe('Traversable laws', 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);
-          return lhs.equals(rhs);
-        }));
-      });
-
-      it('satisfies identity', function() {
-        jsc.assert(jsc.forall(MaybeArb(jsc.integer), function(maybe) {
-          var lhs = maybe.map(Identity).sequence(Identity.of);
-          var rhs = Identity.of(maybe);
-          return lhs.equals(rhs);
-        }));
-      });
-
-      it('satisfies composition', function() {
-        jsc.assert(jsc.forall(MaybeArb(IdentityArb(MaybeArb(jsc.integer))), function(u) {
-          var C = Compose(Identity, S.Maybe);
-          var lhs = u.map(C).sequence(C.of);
-          var rhs = C(u.sequence(Identity.of).map(function(x) {
-            return x.sequence(S.Maybe.of);
-          }));
-          return lhs.equals(rhs);
-        }));
-      });
-
-    });
-
   });
 
   describe('Nothing', function() {
@@ -698,11 +594,6 @@ describe('maybe', function() {
                             'Function as its first argument; received null'));
     });
 
-    it('provides a "sequence" method', function() {
-      eq(S.Nothing().sequence.length, 1);
-      eq(S.Nothing().sequence(S.Either.of), S.Right(S.Nothing()));
-    });
-
     it('provides a "toBoolean" method', function() {
       eq(S.Nothing().toBoolean.length, 0);
       eq(S.Nothing().toBoolean(), false);
@@ -937,11 +828,6 @@ describe('maybe', function() {
                             'Function as its first argument; received null'));
     });
 
-    it('provides a "sequence" method', function() {
-      eq(S.Just(S.Right(42)).sequence.length, 1);
-      eq(S.Just(S.Right(42)).sequence(S.Either.of), S.Right(S.Just(42)));
-    });
-
     it('provides a "toBoolean" method', function() {
       eq(S.Just(42).toBoolean.length, 0);
       eq(S.Just(42).toBoolean(), true);
@@ -3750,6 +3636,52 @@ describe('regexp', function() {
 
 describe('string', function() {
 
+  describe('toUpper', function() {
+
+    it('is a unary function', function() {
+      eq(typeof S.toUpper, 'function');
+      eq(S.toUpper.length, 1);
+    });
+
+    it('type checks its arguments', function() {
+      assert.throws(function() { S.toUpper(true); },
+                    errorEq(TypeError,
+                            '‘toUpper’ expected a value of type String ' +
+                            'as its first argument; received true'));
+    });
+
+    it('returns the upper-case equivalent of its argument', function() {
+      eq(S.toUpper(''), '');
+      eq(S.toUpper('ABC def 123'), 'ABC DEF 123');
+      eq(S.toUpper(new String('')), '');
+      eq(S.toUpper(new String('ABC def 123')), 'ABC DEF 123');
+    });
+
+  });
+
+  describe('toLower', function() {
+
+    it('is a unary function', function() {
+      eq(typeof S.toLower, 'function');
+      eq(S.toLower.length, 1);
+    });
+
+    it('type checks its arguments', function() {
+      assert.throws(function() { S.toLower(true); },
+                    errorEq(TypeError,
+                            '‘toLower’ expected a value of type String ' +
+                            'as its first argument; received true'));
+    });
+
+    it('returns the lower-case equivalent of its argument', function() {
+      eq(S.toLower(''), '');
+      eq(S.toLower('ABC def 123'), 'abc def 123');
+      eq(S.toLower(new String('')), '');
+      eq(S.toLower(new String('ABC def 123')), 'abc def 123');
+    });
+
+  });
+
   describe('words', function() {
 
     it('is a unary function', function() {