diff --git a/index.js b/index.js index 1e9fc7d9..1604964e 100644 --- a/index.js +++ b/index.js @@ -25,7 +25,7 @@ //. Sanctuary gives us a fighting chance of avoiding such errors. We might //. write: //. -//. R.map(S.toUpper, S.head(words)) +//. S.map(S.toUpper, S.head(words)) //. //. Sanctuary is designed to work in Node.js and in ES5-compatible browsers. //. @@ -38,12 +38,6 @@ //. That is, it takes an argument of type `Number` and returns a value of //. type `Number`. //. -//. [`R.map`][R.map] has type `(a -> b) -> Array a -> Array b`. That is, -//. it takes an argument of type `a -> b` and returns a value of type -//. `Array a -> Array b`. `a` and `b` are type variables: applying `R.map` -//. to a value of type `String -> Number` will result in a value of type -//. `Array String -> Array Number`. -//. //. Sanctuary embraces types. JavaScript doesn't support algebraic data types, //. but these can be simulated by providing a group of data constructors which //. return values with the same set of methods. A value of the Either type, for @@ -154,14 +148,16 @@ /* istanbul ignore else */ if (typeof module === 'object' && typeof module.exports === 'object') { - module.exports = f(require('ramda'), require('sanctuary-def')); + module.exports = f(require('ramda'), + require('sanctuary-type-classes'), + require('sanctuary-def')); } else if (typeof define === 'function' && define.amd != null) { - define(['ramda', 'sanctuary-def'], f); + define(['ramda', 'sanctuary-type-classes', 'sanctuary-def'], f); } else { - self.sanctuary = f(self.R, self.sanctuaryDef); + self.sanctuary = f(self.R, self.sanctuaryTypeClasses, self.sanctuaryDef); } -}(function(R, $) { +}(function(R, λ, $) { 'use strict'; @@ -221,71 +217,29 @@ var negativeZero = R.either(R.equals(-0), R.equals(new Number(-0))); // Accessible :: TypeClass - var Accessible = $.TypeClass( + var Accessible = λ.TypeClass( 'sanctuary/Accessible', + [], function(x) { return x != null; } ); - // Applicative :: TypeClass - var Applicative = $.TypeClass( - 'sanctuary/Applicative', - function(x) { - return _type(x) === 'Array' || - Apply._test(x) && (hasMethod(OF)(x) || - hasMethod(OF)(x.constructor)); - } - ); - - // Apply :: TypeClass - var Apply = $.TypeClass( - 'sanctuary/Apply', - function(x) { - return R.contains(_type(x), ['Array', 'Function']) || - Functor._test(x) && hasMethod(AP)(x); - } - ); - - // Foldable :: TypeClass - var Foldable = $.TypeClass( - 'sanctuary/Foldable', - function(x) { - return _type(x) === 'Array' || hasMethod(REDUCE)(x); - } - ); - - // Functor :: TypeClass - var Functor = $.TypeClass( - 'sanctuary/Functor', - function(x) { - return R.contains(_type(x), ['Array', 'Function']) || - hasMethod(MAP)(x); - } - ); - // Monoid :: TypeClass - var Monoid = $.TypeClass( + var Monoid = λ.TypeClass( 'sanctuary/Monoid', + [], function(x) { return R.contains(_type(x), ['Array', 'Boolean', 'Object', 'String']) || - hasMethod(EMPTY)(x); + hasMethod('empty')(x); } ); // Ord :: TypeClass - var Ord = $.TypeClass( + var Ord = λ.TypeClass( 'sanctuary/Ord', + [], R.anyPass([$.String._test, $.ValidDate._test, $.ValidNumber._test]) ); - // Semigroup :: TypeClass - var Semigroup = $.TypeClass( - 'sanctuary/Semigroup', - function(x) { - return R.contains(_type(x), ['Array', 'String']) || - hasMethod(CONCAT)(x); - } - ); - var a = $.TypeVariable('a'); var b = $.TypeVariable('b'); var c = $.TypeVariable('c'); @@ -509,6 +463,195 @@ ); }); + //. ### Fantasy Land + + //# concat :: Semigroup a => a -> a -> a + //. + //. Concatenates two (homogeneous) arrays, two strings, or two values of any + //. other type which satisfies the [Semigroup][] specification. + //. + //. ```javascript + //. > S.concat([1, 2, 3], [4, 5, 6]) + //. [1, 2, 3, 4, 5, 6] + //. + //. > S.concat('foo', 'bar') + //. 'foobar' + //. + //. > S.concat(S.Just('foo'), S.Just('bar')) + //. S.Just('foobar') + //. ``` + var concat = S.concat = + def('concat', + {a: [λ.Semigroup]}, + [a, a, a], + λ.concat); + + //# empty :: Monoid a => a -> a + //. + //. TK. + //. + //. ```javascript + //. > S.empty([1, 2, 3]) + //. [] + //. + //. > S.empty({x: 1, y: 2}) + //. {} + //. + //. > S.empty('abc') + //. '' + //. + //. > S.empty(S.Just(42)) + //. Nothing + //. ``` + S.empty = + def('empty', + {a: [Monoid]}, + [a, a], + λ.empty); + + //# map :: Functor f => (a -> b) -> f a -> f b + //. + //. TK. + //. + //. ```javascript + //. > S.map(S.inc, [1, 2, 3]) + //. [2, 3, 4] + //. + //. > S.map(S.inc, {x: 1, y: 2}) + //. {x: 2, y: 3} + //. + //. > S.map(S.inc, R.length)([1, 2, 3]) + //. 4 + //. + //. > S.map(S.inc, S.Just(42)) + //. Just(43) + //. + //. > S.map(S.inc, S.Right(42)) + //. Right(43) + //. ``` + var map = S.map = + def('map', + {a: [λ.Functor], b: [λ.Functor]}, + [$.AnyFunction, a, b], + λ.map); + + //# reduce :: Foldable f => (a -> b -> a) -> a -> f b -> a + //. + //. Takes a curried binary function, an initial value, and a [Foldable][], + //. and applies the function to the initial value and the Foldable's first + //. value, then applies the function to the result of the previous + //. application and the Foldable's second value. Repeats this process + //. until each of the Foldable's values has been used. Returns the initial + //. value if the Foldable is empty; the result of the final application + //. otherwise. + //. + //. See also [`reduce_`](#reduce_). + //. + //. ```javascript + //. > S.reduce(S.add, 0, [1, 2, 3, 4, 5]) + //. 15 + //. + //. > S.reduce(xs => x => [x].concat(xs), [], [1, 2, 3, 4, 5]) + //. [5, 4, 3, 2, 1] + //. ``` + var reduce = S.reduce = + def('reduce', + {b: [λ.Foldable]}, + [$.AnyFunction, a, b, a], + function(f_, initial, foldable) { + var f = function(a, b) { + return f_(a)(b); + }; + return reduce_(f, initial, foldable); + }); + + //# reduce_ :: Foldable f => ((a, b) -> a) -> a -> f b -> a + //. + //. Version of [`reduce`](#reduce) accepting uncurried functions. + var reduce_ = S.reduce_ = + def('reduce_', + {b: [λ.Foldable]}, + [$.AnyFunction, a, b, a], + λ.reduce); + + //# sequence :: (Applicative f, Traversable t) => (a -> f a) -> t (f a) -> f (t a) + //. + //. TK. + //. + //. ```javascript + //. > S.sequence(S.Maybe.of, [S.Just(1), S.Just(2), S.Just(3)]) + //. Just([1, 2, 3]) + //. + //. > S.sequence(S.Maybe.of, [S.Just(1), S.Just(2), S.Nothing]) + //. Nothing + //. + //. > S.sequence(R.of, S.Just([1, 2, 3])) + //. [Just(1), Just(2), Just(3)] + //. + //. > S.sequence(R.of, S.Nothing) + //. [Nothing] + //. ``` + S.sequence = + def('sequence', + {a: [λ.Traversable], b: [λ.Applicative]}, + [$.AnyFunction, a, b], + λ.sequence); + + //# ap :: Apply f => f (a -> b) -> f a -> f b + //. + //. TK. + //. + //. ```javascript + //. > S.ap([Math.sqrt, S.inc], [1, 4, 9, 16, 25]) + //. [1, 2, 3, 4, 5, 2, 5, 10, 17, 26] + //. + //. > S.ap(x => n => R.repeat(x, n), R.length)('abc') + //. ['abc', 'abc', 'abc'] + //. + //. > S.ap(S.Just(S.inc), S.Just(42)) + //. S.Just(43) + //. ``` + var ap = S.ap = + def('ap', + {a: [λ.Apply], b: [λ.Apply], f: [λ.Apply]}, + [a, b, c], + λ.ap); + + //# chain :: Chain f => (a -> f b) -> f a -> f b + //. + //. TK. + //. + //. ```javascript + //. > S.chain(x => [x, x], [1, 2, 3]) + //. [1, 1, 2, 2, 3, 3] + //. + //. > S.chain(S.parseInt(10), S.Just('42')) + //. Just(42) + //. ``` + var chain = S.chain = + def('chain', + {a: [λ.Chain], b: [λ.Chain]}, + [$.AnyFunction, a, b], + λ.chain); + + //# extend :: Extend e => (e a -> a) -> e a -> e a + //. + //. TK. + S.extend = + def('extend', + {a: [λ.Extend], b: [λ.Extend]}, + [a, b, b], + λ.extend); + + //# extract :: Extend e => e a -> a + //. + //. TK. + S.extract = + def('extract', + {a: [λ.Extend]}, + [a, b], + λ.extract); + //. ### Combinator //# I :: a -> a @@ -535,7 +678,7 @@ //. > S.K('foo', 'bar') //. 'foo' //. - //. > R.map(S.K(42), R.range(0, 5)) + //. > S.map(S.K(42), R.range(0, 5)) //. [42, 42, 42, 42, 42] //. ``` S.K = @@ -554,13 +697,13 @@ //. > S.A(S.inc, 42) //. 43 //. - //. > R.map(S.A(R.__, 100), [S.inc, Math.sqrt]) + //. > S.map(S.A(R.__, 100), [S.inc, Math.sqrt]) //. [101, 10] //. ``` S.A = def('A', {}, - [$.Function, a, b], + [$.AnyFunction, a, b], function(f, x) { return f(x); }); //# T :: a -> (a -> b) -> b @@ -579,7 +722,7 @@ S.T = def('T', {}, - [a, $.Function, b], + [a, $.AnyFunction, b], function(x, f) { return f(x); }); //# C :: (a -> b -> c) -> b -> a -> c @@ -602,7 +745,7 @@ S.C = def('C', {}, - [$.Function, b, a, c], + [$.AnyFunction, b, a, c], function(f, x, y) { return f(y)(x); }); //# B :: (b -> c) -> (a -> b) -> a -> c @@ -619,7 +762,7 @@ S.B = def('B', {}, - [$.Function, $.Function, a, c], + [$.AnyFunction, $.AnyFunction, a, c], compose3); //# S :: (a -> b -> c) -> (a -> b) -> a -> c @@ -637,7 +780,7 @@ S.S = def('S', {}, - [$.Function, $.Function, a, c], + [$.AnyFunction, $.AnyFunction, a, c], function(f, g, x) { return f(x)(g(x)); }); //. ### Function @@ -650,13 +793,13 @@ //. See also [`C`](#C). //. //. ```javascript - //. > R.map(S.flip(Math.pow)(2), [1, 2, 3, 4, 5]) + //. > S.map(S.flip(Math.pow)(2), [1, 2, 3, 4, 5]) //. [1, 4, 9, 16, 25] //. ``` S.flip = def('flip', {}, - [$.Function, b, a, c], + [$.AnyFunction, b, a, c], function(f, x, y) { return f(y, x); }); //# lift :: Functor f => (a -> b) -> f a -> f b @@ -672,9 +815,9 @@ //. ``` S.lift = def('lift', - {a: [Functor], b: [Functor]}, - [$.Function, a, b], - R.map); + {a: [λ.Functor], b: [λ.Functor]}, + [$.AnyFunction, a, b], + map); //# lift2 :: Apply f => (a -> b -> c) -> f a -> f b -> f c //. @@ -696,9 +839,9 @@ //. ``` S.lift2 = def('lift2', - {a: [Apply], b: [Apply], c: [Apply]}, - [$.Function, a, b, c], - function(f, x, y) { return R.ap(R.map(f, x), y); }); + {a: [λ.Apply], b: [λ.Apply], c: [λ.Apply]}, + [$.AnyFunction, a, b, c], + function(f, x, y) { return ap(map(f, x), y); }); //# lift3 :: Apply f => (a -> b -> c -> d) -> f a -> f b -> f c -> f d //. @@ -714,9 +857,9 @@ //. ``` S.lift3 = def('lift3', - {a: [Apply], b: [Apply], c: [Apply], d: [Apply]}, - [$.Function, a, b, c, d], - function(f, x, y, z) { return R.ap(R.ap(R.map(f, x), y), z); }); + {a: [λ.Apply], b: [λ.Apply], c: [λ.Apply], d: [λ.Apply]}, + [$.AnyFunction, a, b, c, d], + function(f, x, y, z) { return ap(ap(map(f, x), y), z); }); //. ### Composition @@ -738,7 +881,7 @@ var compose = S.compose = def('compose', {}, - [$.Function, $.Function, a, c], + [$.AnyFunction, $.AnyFunction, a, c], compose3); //# pipe :: [(a -> b), (b -> c), ..., (m -> n)] -> a -> n @@ -759,7 +902,7 @@ S.pipe = def('pipe', {}, - [$.Array($.Function), a, b], + [$.Array($.AnyFunction), a, b], function(fs, x) { return R.reduceRight(compose2, I, fs)(x); }); //# meld :: [** -> *] -> (* -> * -> ... -> *) @@ -791,9 +934,9 @@ S.meld = def('meld', {}, - [$.Array($.Function), $.Function], + [$.Array($.AnyFunction), $.AnyFunction], function(fs) { - var n = 1 + sum(R.map(R.length, fs)) - fs.length; + var n = 1 + sum(map(R.length, fs)) - fs.length; return R.curryN(n, function() { var args = Array.prototype.slice.call(arguments); for (var idx = 0; idx < fs.length; idx += 1) { @@ -905,7 +1048,7 @@ Maybe.prototype[AP] = method('Maybe#ap', {}, - [$Maybe($.Function), $Maybe(a), $Maybe(b)], + [$Maybe($.AnyFunction), $Maybe(a), $Maybe(b)], function(mf, mx) { return mf.isJust ? mx.map(mf.value) : mf; }); //# Maybe#chain :: Maybe a ~> (a -> Maybe b) -> Maybe b @@ -927,7 +1070,7 @@ Maybe.prototype[CHAIN] = method('Maybe#chain', {}, - [$Maybe(a), $.Function, $Maybe(b)], + [$Maybe(a), $.AnyFunction, $Maybe(b)], function(maybe, f) { return maybe.isJust ? f(maybe.value) : maybe; }); //# Maybe#concat :: Semigroup a => Maybe a ~> Maybe a -> Maybe a @@ -961,7 +1104,7 @@ Maybe.prototype.concat = Maybe.prototype[CONCAT] = method('Maybe#concat', - {a: [Semigroup]}, + {a: [λ.Semigroup]}, [$Maybe(a), $Maybe(a), $Maybe(a)], function(mx, my) { return mx.isNothing ? my : @@ -1036,7 +1179,7 @@ Maybe.prototype[EXTEND] = method('Maybe#extend', {}, - [$Maybe(a), $.Function, $Maybe(a)], + [$Maybe(a), $.AnyFunction, $Maybe(a)], function(maybe, f) { return maybe.isJust ? Just(f(maybe)) : maybe; }); //# Maybe#filter :: Maybe a ~> (a -> Boolean) -> Maybe a @@ -1054,7 +1197,7 @@ Maybe.prototype.filter = method('Maybe#filter', {}, - [$Maybe(a), $.Function, $Maybe(a)], + [$Maybe(a), $.AnyFunction, $Maybe(a)], function(maybe, pred) { return filter(pred, maybe); }); //# Maybe#map :: Maybe a ~> (a -> b) -> Maybe b @@ -1074,7 +1217,7 @@ Maybe.prototype[MAP] = method('Maybe#map', {}, - [$Maybe(a), $.Function, $Maybe(b)], + [$Maybe(a), $.AnyFunction, $Maybe(b)], function(maybe, f) { return maybe.isJust ? Just(f(maybe.value)) : maybe; }); @@ -1114,7 +1257,7 @@ Maybe.prototype[REDUCE] = method('Maybe#reduce', {}, - [$Maybe(a), $.Function, b, b], + [$Maybe(a), $.AnyFunction, b, b], function(maybe, f, x) { return maybe.isJust ? f(x, maybe.value) : x; }); @@ -1140,14 +1283,10 @@ Maybe.prototype.sequence = Maybe.prototype[SEQUENCE] = method('Maybe#sequence', - {a: [Applicative], b: [Applicative]}, - [$Maybe(a), $.Function, b], + {a: [λ.Applicative], b: [λ.Applicative]}, + [$Maybe(a), $.AnyFunction, b], function(maybe, of) { - return maybe.isNothing ? - of(maybe) : - maybe.value != null && typeof maybe.value[MAP] === 'function' ? - maybe.value[MAP](Just) : - R.map(Just, maybe.value); + return maybe.isJust ? map(Just, maybe.value) : of(maybe); }); //# Maybe#toBoolean :: Maybe a ~> Boolean @@ -1331,7 +1470,7 @@ var maybe = S.maybe = def('maybe', {}, - [b, $.Function, $Maybe(a), b], + [b, $.AnyFunction, $Maybe(a), b], function(x, f, maybe) { return fromMaybe(x, maybe.map(f)); }); //# justs :: Array (Maybe a) -> Array a @@ -1349,7 +1488,7 @@ def('justs', {}, [$.Array($Maybe(a)), $.Array(a)], - R.chain(maybe([], R.of))); + chain(maybe([], R.of))); //# mapMaybe :: (a -> Maybe b) -> Array a -> Array b //. @@ -1368,8 +1507,8 @@ S.mapMaybe = def('mapMaybe', {}, - [$.Function, $.Array(a), $.Array(b)], - function(f, xs) { return justs(R.map(f, xs)); }); + [$.AnyFunction, $.Array(a), $.Array(b)], + function(f, xs) { return justs(map(f, xs)); }); //# encase :: (a -> b) -> a -> Maybe b //. @@ -1390,7 +1529,7 @@ var encase = S.encase = def('encase', {}, - [$.Function, a, $Maybe(b)], + [$.AnyFunction, a, $Maybe(b)], function(f, x) { try { return Just(f(x)); @@ -1407,7 +1546,7 @@ var encase2 = S.encase2 = def('encase2', {}, - [$.Function, a, b, $Maybe(c)], + [$.AnyFunction, a, b, $Maybe(c)], function(f, x, y) { try { return Just(f(x)(y)); @@ -1422,7 +1561,7 @@ S.encase2_ = def('encase2_', {}, - [$.Function, a, b, $Maybe(c)], + [$.AnyFunction, a, b, $Maybe(c)], function(f_, x, y) { var f = function(x) { return function(y) { @@ -1440,7 +1579,7 @@ var encase3 = S.encase3 = def('encase3', {}, - [$.Function, a, b, c, $Maybe(d)], + [$.AnyFunction, a, b, c, $Maybe(d)], function(f, x, y, z) { try { return Just(f(x)(y)(z)); @@ -1455,7 +1594,7 @@ S.encase3_ = def('encase3_', {}, - [$.Function, a, b, c, $Maybe(d)], + [$.AnyFunction, a, b, c, $Maybe(d)], function(f_, x, y, z) { var f = function(x) { return function(y) { @@ -1576,7 +1715,7 @@ Either.prototype[AP] = method('Either#ap', {}, - [$Either(a, $.Function), $Either(a, b), $Either(a, c)], + [$Either(a, $.AnyFunction), $Either(a, b), $Either(a, c)], function(ef, ex) { return ef.isRight ? ex.map(ef.value) : ef; }); //# Either#chain :: Either a b ~> (b -> Either a c) -> Either a c @@ -1603,7 +1742,7 @@ Either.prototype[CHAIN] = method('Either#chain', {}, - [$Either(a, b), $.Function, $Either(a, c)], + [$Either(a, b), $.AnyFunction, $Either(a, c)], function(either, f) { return either.isRight ? f(either.value) : either; }); @@ -1640,7 +1779,7 @@ Either.prototype.concat = Either.prototype[CONCAT] = method('Either#concat', - {a: [Semigroup], b: [Semigroup]}, + {a: [λ.Semigroup], b: [λ.Semigroup]}, [$Either(a, b), $Either(a, b), $Either(a, b)], function(ex, ey) { return ex.isLeft === ey.isLeft ? @@ -1695,7 +1834,7 @@ Either.prototype[EXTEND] = method('Either#extend', {}, - [$Either(a, b), $.Function, $Either(a, b)], + [$Either(a, b), $.AnyFunction, $Either(a, b)], function(either, f) { return either.isLeft ? either : Right(f(either)); }); @@ -1717,7 +1856,7 @@ Either.prototype[MAP] = method('Either#map', {}, - [$Either(a, b), $.Function, $Either(a, c)], + [$Either(a, b), $.AnyFunction, $Either(a, c)], function(either, f) { return either.isRight ? Right(f(either.value)) : either; }); @@ -1757,7 +1896,7 @@ Either.prototype[REDUCE] = method('Either#reduce', {}, - [$Either(a, b), $.Function, c, c], + [$Either(a, b), $.AnyFunction, c, c], function(either, f, x) { return either.isRight ? f(x, either.value) : x; }); @@ -1784,14 +1923,10 @@ Either.prototype.sequence = Either.prototype[SEQUENCE] = method('Either#sequence', - {b: [Applicative], c: [Applicative]}, - [$Either(a, b), $.Function, c], + {b: [λ.Applicative], c: [λ.Applicative]}, + [$Either(a, b), $.AnyFunction, c], function(either, of) { - return either.isLeft ? - of(either) : - either.value != null && typeof either.value[MAP] === 'function' ? - either.value[MAP](Right) : - R.map(Right, either.value); + return either.isRight ? map(Right, either.value) : of(either); }); //# Either#toBoolean :: Either a b ~> Boolean @@ -1973,7 +2108,7 @@ S.either = def('either', {}, - [$.Function, $.Function, $Either(a, b), c], + [$.AnyFunction, $.AnyFunction, $Either(a, b), c], function(l, r, either) { return either.isLeft ? l(either.value) : r(either.value); }); @@ -1993,7 +2128,7 @@ def('lefts', {}, [$.Array($Either(a, b)), $.Array(a)], - R.chain(function(either) { + chain(function(either) { return either.isLeft ? [either.value] : []; })); @@ -2012,7 +2147,7 @@ def('rights', {}, [$.Array($Either(a, b)), $.Array(b)], - R.chain(function(either) { + chain(function(either) { return either.isRight ? [either.value] : []; })); @@ -2039,7 +2174,7 @@ S.encaseEither = def('encaseEither', {}, - [$.Function, $.Function, a, $Either(l, r)], + [$.AnyFunction, $.AnyFunction, a, $Either(l, r)], function(f, g, x) { try { return Right(g(x)); @@ -2056,7 +2191,7 @@ var encaseEither2 = S.encaseEither2 = def('encaseEither2', {}, - [$.Function, $.Function, a, b, $Either(l, r)], + [$.AnyFunction, $.AnyFunction, a, b, $Either(l, r)], function(f, g, x, y) { try { return Right(g(x)(y)); @@ -2072,7 +2207,7 @@ S.encaseEither2_ = def('encaseEither2_', {}, - [$.Function, $.Function, a, b, $Either(l, r)], + [$.AnyFunction, $.AnyFunction, a, b, $Either(l, r)], function(f, g_, x, y) { var g = function(x) { return function(y) { @@ -2090,7 +2225,7 @@ var encaseEither3 = S.encaseEither3 = def('encaseEither3', {}, - [$.Function, $.Function, a, b, c, $Either(l, r)], + [$.AnyFunction, $.AnyFunction, a, b, c, $Either(l, r)], function(f, g, x, y, z) { try { return Right(g(x)(y)(z)); @@ -2106,7 +2241,7 @@ S.encaseEither3_ = def('encaseEither3', {}, - [$.Function, $.Function, a, b, c, $Either(l, r)], + [$.AnyFunction, $.AnyFunction, a, b, c, $Either(l, r)], function(f, g_, x, y, z) { var g = function(x) { return function(y) { @@ -2143,8 +2278,9 @@ //. ### Alternative // Alternative :: TypeClass - var Alternative = $.TypeClass( + var Alternative = λ.TypeClass( 'Alternative', + [], function(x) { return R.contains(R.type(x), ['Array', 'Boolean']) || hasMethod('toBoolean')(x); @@ -2272,7 +2408,7 @@ S.ifElse = def('ifElse', {}, - [$.Function, $.Function, $.Function, a, b], + [$.AnyFunction, $.AnyFunction, $.AnyFunction, a, b], function(pred, f, g, x) { return pred(x) ? f(x) : g(x); }); //# allPass :: Array (a -> Boolean) -> a -> Boolean @@ -2292,7 +2428,7 @@ S.allPass = def('allPass', {}, - [$.Array($.Function), a, $.Boolean], + [$.Array($.AnyFunction), a, $.Boolean], function(preds, x) { for (var idx = 0; idx < preds.length; idx += 1) { if (!preds[idx](x)) return false; @@ -2317,7 +2453,7 @@ S.anyPass = def('anyPass', {}, - [$.Array($.Function), a, $.Boolean], + [$.Array($.AnyFunction), a, $.Boolean], function(preds, x) { for (var idx = 0; idx < preds.length; idx += 1) { if (preds[idx](x)) return true; @@ -2333,30 +2469,6 @@ //. //. `[a]` is the notation used to represent a List of values of type `a`. - //# concat :: Semigroup a => a -> a -> a - //. - //. Concatenates two (homogeneous) arrays, two strings, or two values of any - //. other type which satisfies the [Semigroup][] specification. - //. - //. ```javascript - //. > S.concat([1, 2, 3], [4, 5, 6]) - //. [1, 2, 3, 4, 5, 6] - //. - //. > S.concat('foo', 'bar') - //. 'foobar' - //. - //. > S.concat(S.Just('foo'), S.Just('bar')) - //. S.Just('foobar') - //. ``` - var concat = S.concat = - def('concat', - {a: [Semigroup]}, - [a, a, a], - function(x, y) { - return R.contains(R.type(x), ['Array', 'String']) ? x.concat(y) - : x[CONCAT](y); - }); - //# slice :: Integer -> Integer -> [a] -> Maybe [a] //. //. Returns Just a list containing the elements from the supplied list @@ -2420,7 +2532,7 @@ {}, [$.Integer, List(a), $Maybe(a)], function(n, xs) { - return R.map(R.head, slice(n, n === -1 ? -0 : n + 1, xs)); + return map(R.head, slice(n, n === -1 ? -0 : n + 1, xs)); }); //# head :: [a] -> Maybe a @@ -2620,8 +2732,9 @@ }); // ArrayLike :: TypeClass - var ArrayLike = $.TypeClass( + var ArrayLike = λ.TypeClass( 'ArrayLike', + [], function(x) { return x != null && typeof x !== 'function' && @@ -2739,7 +2852,7 @@ S.find = def('find', {}, - [$.Function, $.Array(a), $Maybe(a)], + [$.AnyFunction, $.Array(a), $Maybe(a)], function(pred, xs) { for (var idx = 0, len = xs.length; idx < len; idx += 1) { if (pred(xs[idx])) { @@ -2767,56 +2880,7 @@ def('pluck', {a: [Accessible]}, [TypeRep, $.String, $.Array(a), $.Array($Maybe(b))], - function(type, key, xs) { return R.map(get(type, key), xs); }); - - //# reduce :: Foldable f => (a -> b -> a) -> a -> f b -> a - //. - //. Takes a curried binary function, an initial value, and a [Foldable][], - //. and applies the function to the initial value and the Foldable's first - //. value, then applies the function to the result of the previous - //. application and the Foldable's second value. Repeats this process - //. until each of the Foldable's values has been used. Returns the initial - //. value if the Foldable is empty; the result of the final application - //. otherwise. - //. - //. See also [`reduce_`](#reduce_). - //. - //. ```javascript - //. > S.reduce(S.add, 0, [1, 2, 3, 4, 5]) - //. 15 - //. - //. > S.reduce(xs => x => [x].concat(xs), [], [1, 2, 3, 4, 5]) - //. [5, 4, 3, 2, 1] - //. ``` - var reduce = S.reduce = - def('reduce', - {b: [Foldable]}, - [$.Function, a, b, a], - function(f_, initial, foldable) { - var f = function(a, b) { - return f_(a)(b); - }; - return reduce_(f, initial, foldable); - }); - - //# reduce_ :: Foldable f => ((a, b) -> a) -> a -> f b -> a - //. - //. Version of [`reduce`](#reduce) accepting uncurried functions. - var reduce_ = S.reduce_ = - def('reduce_', - {b: [Foldable]}, - [$.Function, a, b, a], - function(f, initial, foldable) { - if (_type(foldable) === 'Array') { - var acc = initial; - for (var idx = 0; idx < foldable.length; idx += 1) { - acc = f(acc, foldable[idx]); - } - return acc; - } else { - return foldable[REDUCE](f, initial); - } - }); + function(type, key, xs) { return map(get(type, key), xs); }); //# unfoldr :: (b -> Maybe (Pair a b)) -> b -> Array a //. @@ -2837,7 +2901,7 @@ S.unfoldr = def('unfoldr', {}, - [$.Function, b, $.Array(a)], + [$.AnyFunction, b, $.Array(a)], function(f, x) { var result = []; var m = f(x); @@ -3048,7 +3112,7 @@ //. ``` var sum = S.sum = def('sum', - {f: [Foldable]}, + {f: [λ.Foldable]}, [f, $.FiniteNumber], reduce(function(a) { return function(b) { return a + b; }; }, 0)); @@ -3127,7 +3191,7 @@ //. ``` S.product = def('product', - {f: [Foldable]}, + {f: [λ.Foldable]}, [f, $.FiniteNumber], reduce(function(a) { return function(b) { return a * b; }; }, 1)); @@ -3165,7 +3229,7 @@ //. ``` S.mean = def('mean', - {f: [Foldable]}, + {f: [λ.Foldable]}, [f, $Maybe($.FiniteNumber)], function(foldable) { var result = reduce_( @@ -3342,7 +3406,7 @@ def('parseFloat', {}, [$.String, $Maybe($.Number)], - R.pipe(Just, R.filter(validFloatRepr), R.map(parseFloat))); + R.pipe(Just, R.filter(validFloatRepr), map(parseFloat))); //# parseInt :: Integer -> String -> Maybe Integer //. @@ -3384,7 +3448,7 @@ R.all(R.pipe(toUpper, R.indexOf(_, charset), R.gte(_, 0))))), - R.map(R.partialRight(parseInt, [radix])), + map(R.partialRight(parseInt, [radix])), R.filter($.Integer._test) )(s); }); @@ -3490,7 +3554,7 @@ [$.RegExp, $.String, $Maybe($.Array($Maybe($.String)))], function(pattern, s) { var match = s.match(pattern); - return match == null ? Nothing : Just(R.map(toMaybe, match)); + return match == null ? Nothing : Just(λ.map(toMaybe, match)); }); //. ### String @@ -3608,7 +3672,7 @@ def('unlines', {}, [$.Array($.String), $.String], - compose(R.join(''), R.map(concat(_, '\n')))); + compose(R.join(''), map(concat(_, '\n')))); return S; @@ -3631,7 +3695,6 @@ //. [Monoid]: https://github.com/fantasyland/fantasy-land#monoid //. [Nullable]: https://github.com/sanctuary-js/sanctuary-def#nullable //. [R.equals]: http://ramdajs.com/docs/#equals -//. [R.map]: http://ramdajs.com/docs/#map //. [R.type]: http://ramdajs.com/docs/#type //. [Ramda]: http://ramdajs.com/ //. [RegExp]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp diff --git a/package.json b/package.json index bd4b4ffe..e40cdefc 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,8 @@ }, "dependencies": { "ramda": "0.22.x", - "sanctuary-def": "0.6.x" + "sanctuary-def": "sanctuary-js/sanctuary-def#dc-sanctuary-type-classes", + "sanctuary-type-classes": "sanctuary-js/sanctuary-type-classes#dc-everything" }, "devDependencies": { "doctest": "0.10.x", diff --git a/test/Either/Either.js b/test/Either/Either.js index 5e91f170..5d432012 100644 --- a/test/Either/Either.js +++ b/test/Either/Either.js @@ -19,7 +19,7 @@ var SEQUENCE = FantasyLand.sequence; // Identity :: a -> Identity a var Identity = function Identity(x) { - var $ = {value: x}; + var $ = {value: x, toString: R.always('Identity(' + R.toString(x) + ')')}; $[OF] = Identity; $[MAP] = function(fn) { return Identity(fn(x)); }; $[AP] = function(y) { return Identity(x(y)); }; diff --git a/test/I.js b/test/I.js index 6369407e..c99b4f9f 100644 --- a/test/I.js +++ b/test/I.js @@ -14,8 +14,6 @@ describe('I', function() { it('returns its argument', function() { eq(S.I([1, 2, 3]), [1, 2, 3]); eq(S.I(['foo', 42]), ['foo', 42]); - eq(S.I({'@@type': 'my-package/Foreign'}), - {'@@type': 'my-package/Foreign'}); }); }); diff --git a/test/Maybe/Maybe.js b/test/Maybe/Maybe.js index 3af60ba0..a6aaba97 100644 --- a/test/Maybe/Maybe.js +++ b/test/Maybe/Maybe.js @@ -19,7 +19,7 @@ var SEQUENCE = FantasyLand.sequence; // Identity :: a -> Identity a var Identity = function Identity(x) { - var $ = {value: x}; + var $ = {value: x, toString: R.always('Identity(' + R.toString(x) + ')')}; $[OF] = Identity; $[MAP] = function(fn) { return Identity(fn(x)); }; $[AP] = function(y) { return Identity(x(y)); }; diff --git a/test/map.js b/test/map.js new file mode 100644 index 00000000..67cae972 --- /dev/null +++ b/test/map.js @@ -0,0 +1,73 @@ +'use strict'; + +var throws = require('assert').throws; + +var eq = require('./utils').eq; +var errorEq = require('./utils').errorEq; +var S = require('..'); + + +describe('map', function() { + + it('is a binary function', function() { + eq(typeof S.map, 'function'); + eq(S.map.length, 2); + }); + + it('type checks its arguments', function() { + throws(function() { S.map('xxx'); }, + errorEq(TypeError, + 'Invalid value\n' + + '\n' + + 'map :: (Functor a, Functor b) => Function -> a -> b\n' + + ' ^^^^^^^^\n' + + ' 1\n' + + '\n' + + '1) "xxx" :: String\n' + + '\n' + + 'The value at position 1 is not a member of ‘Function’.\n')); + + throws(function() { S.map(S.toUpper, 'xxx'); }, + errorEq(TypeError, + 'Type-class constraint violation\n' + + '\n' + + 'map :: (Functor a, Functor b) => Function -> a -> b\n' + + ' ^^^^^^^^^ ^\n' + + ' 1\n' + + '\n' + + '1) "xxx" :: String\n' + + '\n' + + '‘map’ requires ‘a’ to satisfy the Functor type-class constraint; the value at position 1 does not.\n')); + }); + + it('maps a function into the context of Functors', function() { + eq(S.map(S.not, S.odd)(2), true); + eq(S.map(S.not, S.odd)(3), false); + + eq(S.map(S.mult(4), S.Just(2)), S.Just(8)); + eq(S.map(S.mult(4), S.Nothing), S.Nothing); + + eq(S.map(S.mult(4), S.Left(3)), S.Left(3)); + eq(S.map(S.mult(4), S.Right(2)), S.Right(8)); + + eq(S.map(S.mult(2), [1, 2, 3]), [2, 4, 6]); + eq(S.map(S.mult(2), []), []); + + eq(S.map(S.mult(2), {a: 1, b: 2, c: 3}), {a: 2, b: 4, c: 6}); + eq(S.map(S.mult(2), {}), {}); + }); + + it("does not map over an object's prototype properties", function() { + var Point = function Point(x, y) { + this.x = x; + this.y = y; + }; + + Point.prototype.color = 'black'; + + var point = new Point(0, 0); + + eq(S.map(S.inc, point), {x: 1, y: 1}); + }); + +});