Created
September 19, 2015 00:50
-
-
Save ChetHarrison/440db4e076a0ad7385ac to your computer and use it in GitHub Desktop.
The fab Sanctuary lib wrapped in the UMD
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* ####### | |
#### #### | |
#### ### #### | |
##### ########### sanctuary | |
######## ######## noun | |
########### ##### 1 [ mass noun ] refuge from unsafe JavaScript | |
#### ### #### | |
#### #### | |
####### */ | |
//. # Sanctuary | |
//. | |
//. Sanctuary is a small functional programming library inspired by Haskell | |
//. and PureScript. It depends on and works nicely with [Ramda][]. Sanctuary | |
//. makes it possible to write safe code without null checks. | |
//. | |
//. In JavaScript it's trivial to introduce a possible run-time type error: | |
//. | |
//. words[0].toUpperCase() | |
//. | |
//. If `words` is `[]` we'll get a familiar error at run-time: | |
//. | |
//. TypeError: Cannot read property 'toUpperCase' of undefined | |
//. | |
//. Sanctuary gives us a fighting chance of avoiding such errors. We might | |
//. write: | |
//. | |
//. R.map(R.toUpper, S.head(words)) | |
//. | |
//. ## Types | |
//. | |
//. Sanctuary uses Haskell-like type signatures to describe the types of | |
//. values, including functions. `'foo'`, for example, has type `String`; | |
//. `[1, 2, 3]` has type `[Number]`. The arrow (`->`) is used to express a | |
//. function's type. `Math.abs`, for example, has type `Number -> Number`. | |
//. That is, it takes an argument of type `Number` and returns a value of | |
//. type `Number`. | |
//. | |
//. [`R.map`][R.map] has type `(a -> b) -> [a] -> [b]`. That is, it takes | |
//. an argument of type `a -> b` and returns a value of type `[a] -> [b]`. | |
//. `a` and `b` are type variables: applying `R.map` to a value of type | |
//. `String -> Number` will give a value of type `[String] -> [Number]`. | |
//. | |
//. Sanctuary embraces types. JavaScript doesn't support algebraic data types, | |
//. but these can be simulated by providing a group of constructor functions | |
//. whose prototypes provide the same set of methods. A value of the Maybe | |
//. type, for example, is created via the Nothing constructor or the Just | |
//. constructor. | |
//. | |
//. It's necessary to extend Haskell's notation to describe implicit arguments | |
//. to the *methods* provided by Sanctuary's types. In `x.map(y)`, for example, | |
//. the `map` method takes an implicit argument `x` in addition to the explicit | |
//. argument `y`. The type of the value upon which a method is invoked appears | |
//. at the beginning of the signature, separated from the arguments and return | |
//. value by a squiggly arrow (`~>`). The type of the `map` method of the Maybe | |
//. type is written `Maybe a ~> (a -> b) -> Maybe b`. One could read this as: | |
//. | |
//. _When the `map` method is invoked on a value of type `Maybe a` | |
//. (for any type `a`) with an argument of type `a -> b` (for any type `b`), | |
//. it returns a value of type `Maybe b`._ | |
//. | |
//. ## API | |
;(function(factory) { | |
// Establish the root object, `window` (`self`) in the browser, or `global` on the server. | |
// We use `self` instead of `window` for `WebWorker` support. | |
var root = (typeof self == 'object' && self.self == self && self) || | |
(typeof global == 'object' && global.global == global && global); | |
// Set up Sanctuary appropriately for the environment. Start with AMD. | |
if (typeof define === 'function' && define.amd) { | |
define(['ramda', 'exports'], function(R, exports) { | |
// Export global even in AMD case in case this script is loaded with | |
// others that may still expect a global Sanctuary. | |
root.Sanctuary = factory(root, exports, R); | |
}); | |
// Next for Node.js or CommonJS. | |
} else if (typeof exports !== 'undefined') { | |
var R = require('ramda'); | |
factory(root, exports, R); | |
// Finally, as a browser global. | |
} else { | |
root.Sanctuary = factory(root, {}, root.Ramda); | |
} | |
}(function(root, S, R) { | |
'use strict'; | |
// yank all code above this line and add the | |
var _ = R.__; | |
// placeholder :: a -> Boolean | |
var placeholder = function(x) { | |
return x != null && x['@@functional/placeholder'] === true; | |
}; | |
/* istanbul ignore next */ | |
var Any = function Any() {}; | |
var arity = function(n, f) { | |
switch (n) { | |
case 0: return function() { return f.apply(this, arguments); }; | |
case 1: return function(a) { return f.apply(this, arguments); }; | |
case 2: return function(a, b) { return f.apply(this, arguments); }; | |
case 3: return function(a, b, c) { return f.apply(this, arguments); }; | |
} | |
}; | |
var curry = function(name, typePairs, f) { | |
return arity(typePairs.length, function() { | |
var args = arguments; | |
var $typePairs = []; | |
for (var idx = 0; idx < args.length; idx += 1) { | |
var typePair = typePairs[idx]; | |
if (placeholder(args[idx])) { | |
$typePairs.push(typePair); | |
} else if (typePair[1] !== Any && !R.is(typePair[1], args[idx])) { | |
throw new TypeError(R.join(' ', [ | |
name, | |
'requires a value of type', | |
/^function (\w*)/.exec(typePair[1])[1], | |
'as its', | |
['first', 'second', 'third'][typePair[0]], | |
'argument; received', | |
R.toString(args[idx]) | |
])); | |
} | |
} | |
$typePairs = R.concat($typePairs, typePairs.slice(args.length)); | |
return $typePairs.length === 0 ? | |
f.apply(this, args) : | |
curry(name, $typePairs, function() { | |
var $args = Array.prototype.slice.call(arguments); | |
var val = function(x) { return placeholder(x) ? $args.shift() : x; }; | |
return f.apply(this, R.concat(R.map(val, args), $args)); | |
}); | |
}); | |
}; | |
var def = function(name, types, f) { | |
return curry(name, R.zip(R.range(0, types.length), types), f); | |
}; | |
var extend = function(Child, Parent) { | |
function Ctor() { | |
this.constructor = Child; | |
} | |
Ctor.prototype = Parent.prototype; | |
Child.prototype = new Ctor(); | |
Child.super_ = Parent.prototype; | |
}; | |
var filter = function(pred, m) { | |
return m.chain(function(x) { | |
return pred(x) ? m.of(x) : m.empty(); | |
}); | |
}; | |
// negativeZero :: a -> Boolean | |
var negativeZero = R.identical(-0); | |
var self = function() { return this; }; | |
var toString = function(name) { | |
return def(name + '#toString', [], function() { | |
return name + '(' + R.toString(this.value) + ')'; | |
}); | |
}; | |
//. ### Combinator | |
//# K :: a -> b -> a | |
//. | |
//. The K combinator. Takes two values and returns the first. Equivalent to | |
//. Haskell's `const` function. | |
//. | |
//. ```javascript | |
//. > S.K('foo', 'bar') | |
//. "foo" | |
//. > R.map(S.K(42), R.range(0, 5)) | |
//. [42, 42, 42, 42, 42] | |
//. ``` | |
S.K = def('K', [Any, Any], function(x, y) { | |
return x; | |
}); | |
//. ### Maybe type | |
//# Maybe :: Type | |
//. | |
//. 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][] and [Monad][] specifications. | |
var Maybe = S.Maybe = function Maybe() { | |
throw new Error('Cannot instantiate Maybe'); | |
}; | |
//# Maybe.empty :: -> Maybe a | |
//. | |
//. Returns a Nothing. | |
//. | |
//. ```javascript | |
//. > S.Maybe.empty() | |
//. Nothing() | |
//. ``` | |
Maybe.empty = def('Maybe.empty', [], function() { | |
return new Nothing(); | |
}); | |
//# Maybe.of :: a -> Maybe a | |
//. | |
//. Takes a value of any type and returns a Just with the given value. | |
//. | |
//. ```javascript | |
//. > S.Maybe.of(42) | |
//. Just(42) | |
//. ``` | |
Maybe.of = def('Maybe.of', [Any], function(x) { | |
return new Just(x); | |
}); | |
//# Maybe#ap :: Maybe (a -> b) ~> Maybe a -> Maybe b | |
//. | |
//. Takes a value of type `Maybe a` and returns a Nothing unless `this` | |
//. is a Just *and* the argument is a Just, in which case it returns a | |
//. Just whose value is the result of of applying this Just's value to | |
//. the given Just's value. | |
//. | |
//. ```javascript | |
//. > S.Nothing().ap(S.Just(42)) | |
//. Nothing() | |
//. | |
//. > S.Just(R.inc).ap(S.Nothing()) | |
//. Nothing() | |
//. | |
//. > S.Just(R.inc).ap(S.Just(42)) | |
//. Just(43) | |
//. ``` | |
//# Maybe#chain :: Maybe a ~> (a -> Maybe b) -> Maybe b | |
//. | |
//. Takes a function and returns `this` if `this` is a Nothing; otherwise | |
//. it returns the result of applying the function to this Just's value. | |
//. | |
//. ```javascript | |
//. > S.Nothing().chain(S.parseFloat) | |
//. Nothing() | |
//. | |
//. > S.Just('xxx').chain(S.parseFloat) | |
//. Nothing() | |
//. | |
//. > S.Just('12.34').chain(S.parseFloat) | |
//. Just(12.34) | |
//. ``` | |
//# Maybe#concat :: Maybe a ~> Maybe a -> Maybe a | |
//. | |
//. Returns the result of concatenating two Maybe values of the same type. | |
//. `a` must have a [Semigroup][] (indicated by the presence of a `concat` | |
//. method). | |
//. | |
//. If `this` is a Nothing and the argument is a Nothing, this method returns | |
//. a Nothing. | |
//. | |
//. If `this` is a Just and the argument is a Just, this method returns a | |
//. Just whose value is the result of concatenating this Just's value and | |
//. the given Just's value. | |
//. | |
//. Otherwise, this method returns the Just. | |
//. | |
//. ```javascript | |
//. > S.Nothing().concat(S.Nothing()) | |
//. Nothing() | |
//. | |
//. > S.Just([1, 2, 3]).concat(S.Just([4, 5, 6])) | |
//. Just([1, 2, 3, 4, 5, 6]) | |
//. | |
//. > S.Nothing().concat(S.Just([1, 2, 3])) | |
//. Just([1, 2, 3]) | |
//. | |
//. > S.Just([1, 2, 3]).concat(S.Nothing()) | |
//. Just([1, 2, 3]) | |
//. ``` | |
//# Maybe#empty :: Maybe a ~> Maybe a | |
//. | |
//. Returns a Nothing. | |
//. | |
//. ```javascript | |
//. > S.Just(42).empty() | |
//. Nothing() | |
//. ``` | |
Maybe.prototype.empty = def('Maybe#empty', [], Maybe.empty); | |
//# Maybe#equals :: Maybe a ~> b -> Boolean | |
//. | |
//. Takes a value of any type and returns `true` if: | |
//. | |
//. - it is a Nothing and `this` is a Nothing; or | |
//. | |
//. - it is a Just and `this` is a Just, and their values are equal | |
//. according to [`R.equals`][R.equals]. | |
//. | |
//. ```javascript | |
//. > S.Nothing().equals(S.Nothing()) | |
//. true | |
//. | |
//. > S.Nothing().equals(null) | |
//. false | |
//. | |
//. > S.Just([1, 2, 3]).equals(S.Just([1, 2, 3])) | |
//. true | |
//. | |
//. > S.Just([1, 2, 3]).equals(S.Just([3, 2, 1])) | |
//. false | |
//. | |
//. > S.Just([1, 2, 3]).equals(S.Nothing()) | |
//. false | |
//. ``` | |
//# Maybe#filter :: Maybe a ~> (a -> Boolean) -> Maybe a | |
//. | |
//. Takes a predicate and returns `this` if `this` is a Just whose value | |
//. satisfies the predicate; Nothing otherwise. | |
//. | |
//. ```javascript | |
//. > S.Just(42).filter(function(n) { return n % 2 === 0; }) | |
//. Just(42) | |
//. | |
//. > S.Just(43).filter(function(n) { return n % 2 === 0; }) | |
//. Nothing() | |
//. ``` | |
Maybe.prototype.filter = def('Maybe#filter', [Function], function(pred) { | |
return filter(pred, this); | |
}); | |
//# Maybe#map :: Maybe a ~> (a -> b) -> Maybe b | |
//. | |
//. Takes a function and returns `this` if `this` is a Nothing; otherwise | |
//. it returns a Just whose value is the result of applying the function to | |
//. this Just's value. | |
//. | |
//. ```javascript | |
//. > S.Nothing().map(R.inc) | |
//. Nothing() | |
//. | |
//. > S.Just(42).map(R.inc) | |
//. Just(43) | |
//. ``` | |
//# Maybe#of :: Maybe a ~> b -> Maybe b | |
//. | |
//. Takes a value of any type and returns a Just with the given value. | |
//. | |
//. ```javascript | |
//. > S.Nothing().of(42) | |
//. Just(42) | |
//. ``` | |
Maybe.prototype.of = def('Maybe#of', [Any], Maybe.of); | |
//# Maybe#toBoolean :: Maybe a ~> Boolean | |
//. | |
//. Returns `false` if `this` is a Nothing; `true` if `this` is a Just. | |
//. | |
//. ```javascript | |
//. > S.Nothing().toBoolean() | |
//. false | |
//. | |
//. > S.Just(42).toBoolean() | |
//. true | |
//. ``` | |
//# Maybe#toString :: Maybe a ~> String | |
//. | |
//. Returns the string representation of the Maybe. | |
//. | |
//. ```javascript | |
//. > S.Nothing().toString() | |
//. "Nothing()" | |
//. | |
//. > S.Just([1, 2, 3]).toString() | |
//. "Just([1, 2, 3])" | |
//. ``` | |
//# Maybe#type :: Type | |
//. | |
//. A reference to the Maybe type. Useful for determining whether two | |
//. values such as `S.Nothing()` and `S.Just(42)` are of the same type. | |
Maybe.prototype.type = Maybe; | |
//# Nothing :: -> Maybe a | |
//. | |
//. Returns a Nothing. Though this is a constructor function the `new` | |
//. keyword needn't be used. | |
//. | |
//. ```javascript | |
//. > S.Nothing() | |
//. Nothing() | |
//. ``` | |
var Nothing = S.Nothing = function Nothing() { | |
if (!(this instanceof Nothing)) { | |
return new Nothing(); | |
} | |
}; | |
extend(Nothing, Maybe); | |
// Nothing#ap :: Maybe (a -> b) ~> Maybe a -> Maybe b | |
Nothing.prototype.ap = def('Nothing#ap', [Maybe], self); | |
// Nothing#chain :: Maybe a ~> (a -> Maybe b) -> Maybe b | |
Nothing.prototype.chain = def('Nothing#chain', [Function], self); | |
// Nothing#concat :: Maybe a ~> Maybe a -> Maybe a | |
Nothing.prototype.concat = def('Nothing#concat', [Maybe], R.identity); | |
// Nothing#equals :: Maybe a ~> b -> Boolean | |
Nothing.prototype.equals = def('Nothing#equals', [Any], R.is(Nothing)); | |
// Nothing#map :: Maybe a ~> (a -> b) -> Maybe b | |
Nothing.prototype.map = def('Nothing#map', [Function], self); | |
// Nothing#toBoolean :: Maybe a ~> Boolean | |
Nothing.prototype.toBoolean = def('Nothing#toBoolean', [], R.always(false)); | |
// Nothing#toString :: Maybe a ~> String | |
Nothing.prototype.toString = def('Nothing#toString', [], | |
R.always('Nothing()')); | |
//# Just :: a -> Maybe a | |
//. | |
//. Takes a value of any type and returns a Just with the given value. | |
//. Though this is a constructor function the `new` keyword needn't be | |
//. used. | |
//. | |
//. ```javascript | |
//. > S.Just(42) | |
//. Just(42) | |
//. ``` | |
var Just = S.Just = function Just(value) { | |
if (!(this instanceof Just)) { | |
return new Just(value); | |
} else { | |
this.value = value; | |
} | |
}; | |
extend(Just, Maybe); | |
// Just#ap :: Maybe (a -> b) ~> Maybe a -> Maybe b | |
Just.prototype.ap = def('Just#ap', [Maybe], function(maybe) { | |
return maybe.map(this.value); | |
}); | |
// Just#chain :: Maybe a ~> (a -> Maybe b) -> Maybe b | |
Just.prototype.chain = def('Just#chain', [Function], function(f) { | |
return f(this.value); | |
}); | |
// Just#concat :: Maybe a ~> Maybe a -> Maybe a | |
Just.prototype.concat = def('Just#concat', [Maybe], function(maybe) { | |
return maybe instanceof Just ? Just(this.value.concat(maybe.value)) : this; | |
}); | |
// Just#equals :: Maybe a ~> b -> Boolean | |
Just.prototype.equals = def('Just#equals', [Any], function(x) { | |
return x instanceof Just && R.eqProps('value', x, this); | |
}); | |
// Just#map :: Maybe a ~> (a -> b) -> Maybe b | |
Just.prototype.map = def('Just#map', [Function], function(f) { | |
return new Just(f(this.value)); | |
}); | |
// Just#toBoolean :: Maybe a ~> Boolean | |
Just.prototype.toBoolean = def('Just#toBoolean', [], R.always(true)); | |
// Just#toString :: Maybe a ~> String | |
Just.prototype.toString = toString('Just'); | |
//# fromMaybe :: a -> Maybe a -> a | |
//. | |
//. Takes a default value and a Maybe, and returns the Maybe's value | |
//. if the Maybe is a Just; the default value otherwise. | |
//. | |
//. ```javascript | |
//. > S.fromMaybe(0, S.Just(42)) | |
//. 42 | |
//. | |
//. > S.fromMaybe(0, S.Nothing()) | |
//. 0 | |
//. ``` | |
S.fromMaybe = def('fromMaybe', [Any, Maybe], function(x, maybe) { | |
return maybe instanceof Just ? maybe.value : x; | |
}); | |
//# toMaybe :: a? -> Maybe a | |
//. | |
//. Takes a value and returns Nothing if the value is null or undefined; | |
//. Just the value otherwise. | |
//. | |
//. ```javascript | |
//. > S.toMaybe(null) | |
//. Nothing() | |
//. | |
//. > S.toMaybe(42) | |
//. Just(42) | |
//. ``` | |
var toMaybe = S.toMaybe = | |
def('toMaybe', [Any], R.ifElse(R.isNil, Nothing, Just)); | |
//# encase :: (* -> a) -> (* -> Maybe a) | |
//. | |
//. Takes a function `f` which may throw and returns a curried function | |
//. `g` which will not throw. The result of applying `g` is determined by | |
//. applying `f` to the same arguments: if this succeeds, `g` returns Just | |
//. the result; otherwise `g` returns Nothing. | |
//. | |
//. ```javascript | |
//. > S.encase(eval)('1 + 1') | |
//. Just(2) | |
//. | |
//. > S.encase(eval)('1 +') | |
//. Nothing() | |
//. ``` | |
var encase = S.encase = def('encase', [Function], function(f) { | |
return R.curryN(f.length, function() { | |
try { | |
return Just(f.apply(this, arguments)); | |
} catch (err) { | |
return Nothing(); | |
} | |
}); | |
}); | |
//. ### Either type | |
//# Either :: Type | |
//. | |
//. The Either type represents values with two possibilities: a value of type | |
//. `Either a b` is either a Left whose value is of type `a` or a Right whose | |
//. value is of type `b`. | |
//. | |
//. The Either type satisfies the [Semigroup][] and [Monad][] specifications. | |
var Either = S.Either = function Either() { | |
throw new Error('Cannot instantiate Either'); | |
}; | |
//# Either.of :: b -> Either a b | |
//. | |
//. Takes a value of any type and returns a Right with the given value. | |
//. | |
//. ```javascript | |
//. > S.Either.of(42) | |
//. Right(42) | |
//. ``` | |
Either.of = def('Either.of', [Any], function(x) { | |
return new Right(x); | |
}); | |
//# Either#ap :: Either a (b -> c) ~> Either a b -> Either a c | |
//. | |
//. Takes a value of type `Either a b` and returns a Left unless `this` | |
//. is a Right *and* the argument is a Right, in which case it returns | |
//. a Right whose value is the result of applying this Right's value to | |
//. the given Right's value. | |
//. | |
//. ```javascript | |
//. > S.Left('Cannot divide by zero').ap(S.Right(42)) | |
//. Left("Cannot divide by zero") | |
//. | |
//. > S.Right(R.inc).ap(S.Left('Cannot divide by zero')) | |
//. Left("Cannot divide by zero") | |
//. | |
//. > S.Right(R.inc).ap(S.Right(42)) | |
//. Right(43) | |
//. ``` | |
//# Either#chain :: Either a b ~> (b -> Either a c) -> Either a c | |
//. | |
//. Takes a function and returns `this` if `this` is a Left; otherwise | |
//. it returns the result of applying the function to this Right's value. | |
//. | |
//. ```javascript | |
//. > void (sqrt = function(n) { return n < 0 ? S.Left('Cannot represent square root of negative number') : S.Right(Math.sqrt(n)); }) | |
//. undefined | |
//. | |
//. > S.Left('Cannot divide by zero').chain(sqrt) | |
//. Left("Cannot divide by zero") | |
//. | |
//. > S.Right(-1).chain(sqrt) | |
//. Left("Cannot represent square root of negative number") | |
//. | |
//. > S.Right(25).chain(sqrt) | |
//. Right(5) | |
//. ``` | |
//# Either#concat :: Either a b ~> Either a b -> Either a b | |
//. | |
//. Returns the result of concatenating two Either values of the same type. | |
//. `a` must have a [Semigroup][] (indicated by the presence of a `concat` | |
//. method), as must `b`. | |
//. | |
//. If `this` is a Left and the argument is a Left, this method returns a | |
//. Left whose value is the result of concatenating this Left's value and | |
//. the given Left's value. | |
//. | |
//. If `this` is a Right and the argument is a Right, this method returns a | |
//. Right whose value is the result of concatenating this Right's value and | |
//. the given Right's value. | |
//. | |
//. Otherwise, this method returns the Right. | |
//. | |
//. ```javascript | |
//. > S.Left('abc').concat(S.Left('def')) | |
//. Left("abcdef") | |
//. | |
//. > S.Right([1, 2, 3]).concat(S.Right([4, 5, 6])) | |
//. Right([1, 2, 3, 4, 5, 6]) | |
//. | |
//. > S.Left('abc').concat(S.Right([1, 2, 3])) | |
//. Right([1, 2, 3]) | |
//. | |
//. > S.Right([1, 2, 3]).concat(S.Left('abc')) | |
//. Right([1, 2, 3]) | |
//. ``` | |
//# Either#equals :: Either a b ~> c -> Boolean | |
//. | |
//. Takes a value of any type and returns `true` if: | |
//. | |
//. - it is a Left and `this` is a Left, and their values are equal | |
//. according to [`R.equals`][R.equals]; or | |
//. | |
//. - it is a Right and `this` is a Right, and their values are equal | |
//. according to [`R.equals`][R.equals]. | |
//. | |
//. ```javascript | |
//. > S.Right([1, 2, 3]).equals(S.Right([1, 2, 3])) | |
//. true | |
//. | |
//. > S.Right([1, 2, 3]).equals(S.Left([1, 2, 3])) | |
//. false | |
//. | |
//. > S.Right(42).equals(42) | |
//. false | |
//. ``` | |
//# Either#map :: Either a b ~> (b -> c) -> Either a c | |
//. | |
//. Takes a function and returns `this` if `this` is a Left; otherwise it | |
//. returns a Right whose value is the result of applying the function to | |
//. this Right's value. | |
//. | |
//. ```javascript | |
//. > S.Left('Cannot divide by zero').map(R.inc) | |
//. Left("Cannot divide by zero") | |
//. | |
//. > S.Right(42).map(R.inc) | |
//. Right(43) | |
//. ``` | |
//# Either#of :: Either a b ~> b -> Either a b | |
//. | |
//. Takes a value of any type and returns a Right with the given value. | |
//. | |
//. ```javascript | |
//. > S.Left('Cannot divide by zero').of(42) | |
//. Right(42) | |
//. ``` | |
Either.prototype.of = def('Either#of', [Any], Either.of); | |
//# Either#toBoolean :: Either a b ~> Boolean | |
//. | |
//. Returns `false` if `this` is a Left; `true` if `this` is a Right. | |
//. | |
//. ```javascript | |
//. > S.Left(42).toBoolean() | |
//. false | |
//. | |
//. > S.Right(42).toBoolean() | |
//. true | |
//. ``` | |
//# Either#toString :: Either a b ~> String | |
//. | |
//. Returns the string representation of the Either. | |
//. | |
//. ```javascript | |
//. > S.Left('Cannot divide by zero').toString() | |
//. "Left(\\"Cannot divide by zero\\")" | |
//. | |
//. > S.Right([1, 2, 3]).toString() | |
//. "Right([1, 2, 3])" | |
//. ``` | |
//# Either#type :: Type | |
//. | |
//. A reference to the Either type. Useful for determining whether two | |
//. values such as `S.Left('Cannot divide by zero')` and `S.Right(42)` | |
//. are of the same type. | |
Either.prototype.type = Either; | |
//# Left :: a -> Either a b | |
//. | |
//. Takes a value of any type and returns a Left with the given value. | |
//. Though this is a constructor function the `new` keyword needn't be | |
//. used. | |
//. | |
//. ```javascript | |
//. > S.Left('Cannot divide by zero') | |
//. Left("Cannot divide by zero") | |
//. ``` | |
var Left = S.Left = function Left(value) { | |
if (!(this instanceof Left)) { | |
return new Left(value); | |
} | |
this.value = value; | |
}; | |
extend(Left, Either); | |
// Left#ap :: Either a (b -> c) ~> Either a b -> Either a c | |
Left.prototype.ap = def('Left#ap', [Either], self); | |
// Left#chain :: Either a b ~> (b -> Either a c) -> Either a c | |
Left.prototype.chain = def('Left#chain', [Function], self); | |
// Left#concat :: Either a b ~> Either a b -> Either a b | |
Left.prototype.concat = def('Left#concat', [Either], function(either) { | |
return R.is(Left, either) ? Left(this.value.concat(either.value)) : either; | |
}); | |
// Left#equals :: Either a b ~> c -> Boolean | |
Left.prototype.equals = def('Left#equals', [Any], function(x) { | |
return x instanceof Left && R.eqProps('value', x, this); | |
}); | |
// Left#map :: Either a b ~> (b -> c) -> Either a c | |
Left.prototype.map = def('Left#map', [Function], self); | |
// Left#toBoolean :: Either a b ~> Boolean | |
Left.prototype.toBoolean = def('Left#toBoolean', [], R.always(false)); | |
// Left#toString :: Either a b ~> String | |
Left.prototype.toString = toString('Left'); | |
//# Right :: b -> Either a b | |
//. | |
//. Takes a value of any type and returns a Right with the given value. | |
//. Though this is a constructor function the `new` keyword needn't be | |
//. used. | |
//. | |
//. ```javascript | |
//. > S.Right(42) | |
//. Right(42) | |
//. ``` | |
var Right = S.Right = function Right(value) { | |
if (!(this instanceof Right)) { | |
return new Right(value); | |
} | |
this.value = value; | |
}; | |
extend(Right, Either); | |
// Right#ap :: Either a (b -> c) ~> Either a b -> Either a c | |
Right.prototype.ap = def('Right#ap', [Either], function(either) { | |
return either.map(this.value); | |
}); | |
// Right#chain :: Either a b ~> (b -> Either a c) -> Either a c | |
Right.prototype.chain = def('Right#chain', [Function], function(f) { | |
return f(this.value); | |
}); | |
// Right#concat :: Either a b ~> Either a b -> Either a b | |
Right.prototype.concat = def('Right#concat', [Either], function(either) { | |
return R.is(Right, either) ? Right(this.value.concat(either.value)) : this; | |
}); | |
// Right#equals :: Either a b ~> c -> Boolean | |
Right.prototype.equals = def('Right#equals', [Any], function(x) { | |
return x instanceof Right && R.eqProps('value', x, this); | |
}); | |
// Right#map :: Either a b ~> (b -> c) -> Either a c | |
Right.prototype.map = def('Right#map', [Function], function(f) { | |
return new Right(f(this.value)); | |
}); | |
// Right#toBoolean :: Either a b ~> Boolean | |
Right.prototype.toBoolean = def('Right#toBoolean', [], R.always(true)); | |
// Right#toString :: Either a b ~> String | |
Right.prototype.toString = toString('Right'); | |
//# either :: (a -> c) -> (b -> c) -> Either a b -> c | |
//. | |
//. Takes two functions and an Either, and returns the result of | |
//. applying the first function to the Left's value, if the Either | |
//. is a Left, or the result of applying the second function to the | |
//. Right's value, if the Either is a Right. | |
//. | |
//. ```javascript | |
//. > S.either(R.toUpper, R.toString, S.Left('Cannot divide by zero')) | |
//. "CANNOT DIVIDE BY ZERO" | |
//. | |
//. > S.either(R.toUpper, R.toString, S.Right(42)) | |
//. "42" | |
//. ``` | |
S.either = | |
def('either', [Function, Function, Either], function(l, r, either) { | |
return either instanceof Left ? l(either.value) : r(either.value); | |
}); | |
//. ### Control | |
var assertTypeMatch = function(x, y) { | |
if (R.type(x) !== R.type(y) || x.type !== y.type) { | |
throw new TypeError('Type mismatch'); | |
} | |
}; | |
// toBoolean :: * -> Boolean | |
var toBoolean = function(x) { | |
if (R.is(Array, x)) return x.length > 0; | |
if (R.is(Boolean, x)) return x; | |
if (R.is(Function, x.toBoolean)) return x.toBoolean(); | |
throw new TypeError(R.toString(x) + ' does not have a "toBoolean" method'); | |
}; | |
// empty :: a -> a | |
var empty = function(x) { | |
if (R.is(Array, x)) return []; | |
if (R.is(Boolean, x)) return false; | |
if (R.is(Function, x.empty)) return x.empty(); | |
throw new TypeError(R.toString(x) + ' does not have an "empty" method'); | |
}; | |
//# and :: a -> a -> a | |
//. | |
//. Takes two values of the same type and returns the second value | |
//. if the first is "true"; the first value otherwise. An array is | |
//. considered "true" if its length is greater than zero. The Boolean | |
//. value `true` is also considered "true". Other types must provide | |
//. a `toBoolean` method. | |
//. | |
//. ```javascript | |
//. > S.and(S.Just(1), S.Just(2)) | |
//. Just(2) | |
//. | |
//. > S.and(S.Nothing(), S.Just(3)) | |
//. Nothing() | |
//. ``` | |
S.and = def('and', [Any, Any], function(x, y) { | |
assertTypeMatch(x, y); | |
return toBoolean(x) ? y : x; | |
}); | |
//# or :: a -> a -> a | |
//. | |
//. Takes two values of the same type and returns the first value if it | |
//. is "true"; the second value otherwise. An array is considered "true" | |
//. if its length is greater than zero. The Boolean value `true` is also | |
//. considered "true". Other types must provide a `toBoolean` method. | |
//. | |
//. ```javascript | |
//. > S.or(S.Just(1), S.Just(2)) | |
//. Just(1) | |
//. | |
//. > S.or(S.Nothing(), S.Just(3)) | |
//. Just(3) | |
//. ``` | |
var or = S.or = def('or', [Any, Any], function(x, y) { | |
assertTypeMatch(x, y); | |
return toBoolean(x) ? x : y; | |
}); | |
//# xor :: a -> a -> a | |
//. | |
//. Takes two values of the same type and returns the "true" value | |
//. if one value is "true" and the other is "false"; otherwise it | |
//. returns the type's "false" value. An array is considered "true" | |
//. if its length is greater than zero. The Boolean value `true` is | |
//. also considered "true". Other types must provide `toBoolean` and | |
//. `empty` methods. | |
//. | |
//. ```javascript | |
//. > S.xor(S.Nothing(), S.Just(1)) | |
//. Just(1) | |
//. | |
//. > S.xor(S.Just(2), S.Just(3)) | |
//. Nothing() | |
//. ``` | |
S.xor = def('xor', [Any, Any], function(x, y) { | |
assertTypeMatch(x, y); | |
var xBool = toBoolean(x); | |
var yBool = toBoolean(y); | |
var xEmpty = empty(x); | |
return xBool !== yBool ? or(x, y) : xEmpty; | |
}); | |
//. ### List | |
//# slice :: Number -> Number -> [a] -> Maybe [a] | |
//. | |
//. Returns Just a list containing the elements from the supplied list | |
//. from a beginning index (inclusive) to an end index (exclusive). | |
//. Returns Nothing unless the start interval is less than or equal to | |
//. the end interval, and the list contains both (half-open) intervals. | |
//. Accepts negative indices, which indicate an offset from the end of | |
//. the list. | |
//. | |
//. Dispatches to its third argument's `slice` method if present. As a | |
//. result, one may replace `[a]` with `String` in the type signature. | |
//. | |
//. ```javascript | |
//. > S.slice(1, 3, ['a', 'b', 'c', 'd', 'e']) | |
//. Just(["b", "c"]) | |
//. | |
//. > S.slice(-2, -0, ['a', 'b', 'c', 'd', 'e']) | |
//. Just(["d", "e"]) | |
//. | |
//. > S.slice(2, -0, ['a', 'b', 'c', 'd', 'e']) | |
//. Just(["c", "d", "e"]) | |
//. | |
//. > S.slice(1, 6, ['a', 'b', 'c', 'd', 'e']) | |
//. Nothing() | |
//. | |
//. > S.slice(2, 6, 'banana') | |
//. Just("nana") | |
//. ``` | |
var slice = S.slice = | |
def('slice', [Number, Number, Any], function(start, end, xs) { | |
var len = xs.length; | |
var startIdx = negativeZero(start) ? len : start < 0 ? start + len : start; | |
var endIdx = negativeZero(end) ? len : end < 0 ? end + len : end; | |
return (Math.abs(start) <= len && Math.abs(end) <= len && startIdx <= endIdx) ? | |
Just(R.slice(startIdx, endIdx, xs)) : | |
Nothing(); | |
}); | |
//# at :: Number -> [a] -> Maybe a | |
//. | |
//. Takes an index and a list and returns Just the element of the list at | |
//. the index if the index is within the list's bounds; Nothing otherwise. | |
//. A negative index represents an offset from the length of the list. | |
//. | |
//. ```javascript | |
//. > S.at(2, ['a', 'b', 'c', 'd', 'e']) | |
//. Just("c") | |
//. | |
//. > S.at(5, ['a', 'b', 'c', 'd', 'e']) | |
//. Nothing() | |
//. | |
//. > S.at(-2, ['a', 'b', 'c', 'd', 'e']) | |
//. Just("d") | |
//. ``` | |
var at = S.at = def('at', [Number, Any], function(n, xs) { | |
return R.map(R.head, slice(n, n === -1 ? -0 : n + 1, xs)); | |
}); | |
//# head :: [a] -> Maybe a | |
//. | |
//. Takes a list and returns Just the first element of the list if the | |
//. list contains at least one element; Nothing if the list is empty. | |
//. | |
//. ```javascript | |
//. > S.head([1, 2, 3]) | |
//. Just(1) | |
//. | |
//. > S.head([]) | |
//. Nothing() | |
//. ``` | |
S.head = def('head', [Any], at(0)); | |
//# last :: [a] -> Maybe a | |
//. | |
//. Takes a list and returns Just the last element of the list if the | |
//. list contains at least one element; Nothing if the list is empty. | |
//. | |
//. ```javascript | |
//. > S.last([1, 2, 3]) | |
//. Just(3) | |
//. | |
//. > S.last([]) | |
//. Nothing() | |
//. ``` | |
S.last = def('last', [Any], at(-1)); | |
//# tail :: [a] -> Maybe [a] | |
//. | |
//. Takes a list and returns Just a list containing all but the first | |
//. of the list's elements if the list contains at least one element; | |
//. Nothing if the list is empty. | |
//. | |
//. ```javascript | |
//. > S.tail([1, 2, 3]) | |
//. Just([2, 3]) | |
//. | |
//. > S.tail([]) | |
//. Nothing() | |
//. ``` | |
S.tail = def('tail', [Any], slice(1, -0)); | |
//# init :: [a] -> Maybe [a] | |
//. | |
//. Takes a list and returns Just a list containing all but the last | |
//. of the list's elements if the list contains at least one element; | |
//. Nothing if the list is empty. | |
//. | |
//. ```javascript | |
//. > S.init([1, 2, 3]) | |
//. Just([1, 2]) | |
//. | |
//. > S.init([]) | |
//. Nothing() | |
//. ``` | |
S.init = def('init', [Any], slice(0, -1)); | |
//# take :: Number -> [a] -> Maybe [a] | |
//. | |
//. Returns Just the first N elements of the given collection if N is | |
//. greater than or equal to zero and less than or equal to the length | |
//. of the collection; Nothing otherwise. Supports Array, String, and | |
//. any other collection type which provides a `slice` method. | |
//. | |
//. ```javascript | |
//. > S.take(2, ['a', 'b', 'c', 'd', 'e']) | |
//. Just(["a", "b"]) | |
//. | |
//. > S.take(4, 'abcdefg') | |
//. Just("abcd") | |
//. | |
//. > S.take(4, ['a', 'b', 'c']) | |
//. Nothing() | |
//. ``` | |
S.take = def('take', [Number, Any], function(n, xs) { | |
return n < 0 || negativeZero(n) ? Nothing() : slice(0, n, xs); | |
}); | |
//# drop :: Number -> [a] -> Maybe [a] | |
//. | |
//. Returns Just all but the first N elements of the given collection | |
//. if N is greater than or equal to zero and less than or equal to the | |
//. length of the collection; Nothing otherwise. Supports Array, String, | |
//. and any other collection type which provides a `slice` method. | |
//. | |
//. ```javascript | |
//. > S.drop(2, ['a', 'b', 'c', 'd', 'e']) | |
//. Just(["c", "d", "e"]) | |
//. | |
//. > S.drop(4, 'abcdefg') | |
//. Just("efg") | |
//. | |
//. > S.drop(4, 'abc') | |
//. Nothing() | |
//. ``` | |
S.drop = def('drop', [Number, Any], function(n, xs) { | |
return n < 0 || negativeZero(n) ? Nothing() : slice(n, -0, xs); | |
}); | |
//# find :: (a -> Boolean) -> [a] -> Maybe a | |
//. | |
//. Takes a predicate and a list and returns Just the leftmost element of | |
//. the list which satisfies the predicate; Nothing if none of the list's | |
//. elements satisfies the predicate. | |
//. | |
//. ```javascript | |
//. > S.find(function(n) { return n < 0; }, [1, -2, 3, -4, 5]) | |
//. Just(-2) | |
//. | |
//. > S.find(function(n) { return n < 0; }, [1, 2, 3, 4, 5]) | |
//. Nothing() | |
//. ``` | |
S.find = def('find', [Function, Any], function(pred, xs) { | |
for (var idx = 0, len = xs.length; idx < len; idx += 1) { | |
if (pred(xs[idx])) { | |
return Just(xs[idx]); | |
} | |
} | |
return Nothing(); | |
}); | |
var sanctifyIndexOf = function(name) { | |
return def(name, [Any, Any], R.pipe(R[name], Just, R.filter(R.gte(_, 0)))); | |
}; | |
//# indexOf :: a -> [a] -> Maybe Number | |
//. | |
//. Takes a value of any type and a list, and returns Just the index | |
//. of the first occurrence of the value in the list, if applicable; | |
//. Nothing otherwise. | |
//. | |
//. Dispatches to its second argument's `indexOf` method if present. | |
//. As a result, `String -> String -> Maybe Number` is an alternative | |
//. type signature. | |
//. | |
//. ```javascript | |
//. > S.indexOf('a', ['b', 'a', 'n', 'a', 'n', 'a']) | |
//. Just(1) | |
//. | |
//. > S.indexOf('x', ['b', 'a', 'n', 'a', 'n', 'a']) | |
//. Nothing() | |
//. | |
//. > S.indexOf('an', 'banana') | |
//. Just(1) | |
//. | |
//. > S.indexOf('ax', 'banana') | |
//. Nothing() | |
//. ``` | |
S.indexOf = sanctifyIndexOf('indexOf'); | |
//# lastIndexOf :: a -> [a] -> Maybe Number | |
//. | |
//. Takes a value of any type and a list, and returns Just the index | |
//. of the last occurrence of the value in the list, if applicable; | |
//. Nothing otherwise. | |
//. | |
//. Dispatches to its second argument's `lastIndexOf` method if present. | |
//. As a result, `String -> String -> Maybe Number` is an alternative | |
//. type signature. | |
//. | |
//. ```javascript | |
//. > S.lastIndexOf('a', ['b', 'a', 'n', 'a', 'n', 'a']) | |
//. Just(5) | |
//. | |
//. > S.lastIndexOf('x', ['b', 'a', 'n', 'a', 'n', 'a']) | |
//. Nothing() | |
//. | |
//. > S.lastIndexOf('an', 'banana') | |
//. Just(3) | |
//. | |
//. > S.lastIndexOf('ax', 'banana') | |
//. Nothing() | |
//. ``` | |
S.lastIndexOf = sanctifyIndexOf('lastIndexOf'); | |
//# pluck :: String -> [{String: *}] -> [Maybe *] | |
//. | |
//. Takes a list of objects and plucks the value of the specified key | |
//. for each object in the list. Returns the value wrapped in a Just | |
//. if an object has the key and a Nothing if it does not. | |
//. | |
//. ```javascript | |
//. > S.pluck('a', [{a: 1, b: 2}, {a: 4, b: 5}, {b: 3, c: 7}]) | |
//. [Just(1), Just(4), Nothing()] | |
//. | |
//. > S.pluck('x', [{x: 1}, {x: 2}, {x: undefined}]) | |
//. [Just(1), Just(2), Just(undefined)] | |
//. ``` | |
S.pluck = def('pluck', [String, Any], function(key, xs) { | |
return R.map(get(key), xs); | |
}); | |
//. ### Object | |
//# get :: String -> Object -> Maybe * | |
//. | |
//. Takes a property name and an object and returns Just the value of | |
//. the specified property of the object if the object has such an own | |
//. property; Nothing otherwise. | |
//. | |
//. ```javascript | |
//. > S.get('x', {x: 1, y: 2}) | |
//. Just(1) | |
//. | |
//. > S.get('toString', {x: 1, y: 2}) | |
//. Nothing() | |
//. ``` | |
var get = S.get = | |
def('get', [String, Any], | |
R.ifElse(R.has, R.compose(Just, R.prop), Nothing)); | |
//# gets :: [String] -> Object -> Maybe * | |
//. | |
//. Takes a list of property names and an object and returns Just the | |
//. value at the path specified by the list of property names if such | |
//. a path exists; Nothing otherwise. | |
//. | |
//. ```javascript | |
//. > S.gets(['a', 'b', 'c'], {a: {b: {c: 42}}}) | |
//. Just(42) | |
//. | |
//. > S.gets(['a', 'b', 'c'], {}) | |
//. Nothing() | |
//. ``` | |
S.gets = def('gets', [Any, Any], function(keys, obj) { | |
return R.reduce(function(acc, key) { | |
return R.chain(get(key), acc); | |
}, Just(obj), keys); | |
}); | |
//. ### Parse | |
//# parseDate :: String -> Maybe Date | |
//. | |
//. Takes a string and returns Just the date represented by the string | |
//. if it does in fact represent a date; Nothing otherwise. | |
//. | |
//. ```javascript | |
//. > S.parseDate('2011-01-19T17:40:00Z') | |
//. Just(new Date("2011-01-19T17:40:00.000Z")) | |
//. | |
//. > S.parseDate('today') | |
//. Nothing() | |
//. ``` | |
S.parseDate = def('parseDate', [String], function(s) { | |
var d = new Date(s); | |
return d.valueOf() === d.valueOf() ? Just(d) : Nothing(); | |
}); | |
//# parseFloat :: String -> Maybe Number | |
//. | |
//. Takes a string and returns Just the number represented by the string | |
//. if it does in fact represent a number; Nothing otherwise. | |
//. | |
//. ```javascript | |
//. > S.parseFloat('-123.45') | |
//. Just(-123.45) | |
//. | |
//. > S.parseFloat('foo.bar') | |
//. Nothing() | |
//. ``` | |
S.parseFloat = def('parseFloat', [String], function(s) { | |
var n = parseFloat(s); | |
return n === n ? Just(n) : Nothing(); | |
}); | |
//# parseInt :: Number -> String -> Maybe Number | |
//. | |
//. Takes a radix (an integer between 2 and 36 inclusive) and a string, | |
//. and returns Just the number represented by the string if it does in | |
//. fact represent a number in the base specified by the radix; Nothing | |
//. otherwise. | |
//. | |
//. This function is stricter than [`parseInt`][parseInt]: a string | |
//. is considered to represent an integer only if all its non-prefix | |
//. characters are members of the character set specified by the radix. | |
//. | |
//. ```javascript | |
//. > S.parseInt(10, '-42') | |
//. Just(-42) | |
//. | |
//. > S.parseInt(16, '0xFF') | |
//. Just(255) | |
//. | |
//. > S.parseInt(16, '0xGG') | |
//. Nothing() | |
//. ``` | |
S.parseInt = def('parseInt', [Number, String], function(radix, s) { | |
if (radix < 2 || radix > 36) { | |
throw new RangeError('Radix not in [2 .. 36]'); | |
} | |
var charset = R.take(radix, '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'); | |
return R.pipe( | |
Just, | |
R.filter(R.pipe(R.replace(/^[+-]/, ''), | |
radix === 16 ? R.replace(/^0x/i, '') : R.identity, | |
R.split(''), | |
R.all(R.pipe(R.toUpper, | |
R.indexOf(_, charset), | |
R.gte(_, 0))))), | |
R.map(R.partialRight(parseInt, radix)) | |
)(s); | |
}); | |
//# parseJson :: String -> Maybe * | |
//. | |
//. Takes a string which may or may not be valid JSON, and returns Just | |
//. the result of applying `JSON.parse` to the string if valid; Nothing | |
//. otherwise. | |
//. | |
//. ```javascript | |
//. > S.parseJson('["foo","bar","baz"]') | |
//. Just(["foo", "bar", "baz"]) | |
//. | |
//. > S.parseJson('[') | |
//. Nothing() | |
//. ``` | |
S.parseJson = def('parseJson', [String], encase(function(s) { | |
return JSON.parse(s); | |
})); | |
//. ### RegExp | |
//# match :: RegExp -> String -> Maybe [Maybe String] | |
//. | |
//. Takes a pattern and a string, and returns Just a list of matches | |
//. if the pattern matches the string; Nothing otherwise. Each match | |
//. has type `Maybe String`, where a Nothing represents an unmatched | |
//. optional capturing group. | |
//. | |
//. ```javascript | |
//. > S.match(/(good)?bye/, 'goodbye') | |
//. Just([Just("goodbye"), Just("good")]) | |
//. | |
//. > S.match(/(good)?bye/, 'bye') | |
//. Just([Just("bye"), Nothing()]) | |
//. ``` | |
S.match = | |
def('match', [RegExp, String], | |
R.compose(R.map(R.map(toMaybe)), toMaybe, R.match)); | |
return S; | |
})); | |
//. [Monad]: https://github.com/fantasyland/fantasy-land#monad | |
//. [Monoid]: https://github.com/fantasyland/fantasy-land#monoid | |
//. [R.equals]: http://ramdajs.com/docs/#equals | |
//. [R.map]: http://ramdajs.com/docs/#map | |
//. [Ramda]: http://ramdajs.com/ | |
//. [Semigroup]: https://github.com/fantasyland/fantasy-land#semigroup | |
//. [parseInt]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
line 1320 is what I was talking about on the return