diff --git a/bower.json b/bower.json index 7bb86398..a97fbb4e 100644 --- a/bower.json +++ b/bower.json @@ -23,7 +23,7 @@ ], "dependencies": { "sanctuary-def": "0.9.0", - "sanctuary-type-classes": "2.0.1", + "sanctuary-type-classes": "3.0.0", "sanctuary-type-identifiers": "1.0.0" }, "ignore": [ diff --git a/index.js b/index.js index 2c714a27..8a0466ea 100644 --- a/index.js +++ b/index.js @@ -30,7 +30,7 @@ //. Sanctuary gives us a fighting chance of avoiding such errors. We might //. write: //. -//. Z.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. //. @@ -286,6 +286,10 @@ var f = $.UnaryTypeVariable('f'); var l = $.TypeVariable('l'); var r = $.TypeVariable('r'); + var m = $.UnaryTypeVariable('m'); + var p = $.BinaryTypeVariable('p'); + var t = $.UnaryTypeVariable('t'); + var w = $.UnaryTypeVariable('w'); // eitherTypeIdent :: String var eitherTypeIdent = 'sanctuary/Either'; @@ -452,10 +456,10 @@ //. The special [placeholder](#placeholder) value. //. //. ```javascript - //. > Z.map(S.concat('@'), ['foo', 'bar', 'baz']) + //. > S.map(S.concat('@'), ['foo', 'bar', 'baz']) //. ['@foo', '@bar', '@baz'] //. - //. > Z.map(S.concat(S.__, '?'), ['foo', 'bar', 'baz']) + //. > S.map(S.concat(S.__, '?'), ['foo', 'bar', 'baz']) //. ['foo?', 'bar?', 'baz?'] //. ``` S.__ = $.__; @@ -502,6 +506,455 @@ } S.is = def('is', {}, [TypeRep(a), $.Any, $.Boolean], is); + //. ### Showable + + //# toString :: Any -> String + //. + //. Alias of [`Z.toString`][]. + //. + //. ```javascript + //. > S.toString(-0) + //. '-0' + //. + //. > S.toString(['foo', 'bar', 'baz']) + //. '["foo", "bar", "baz"]' + //. + //. > S.toString({x: 1, y: 2, z: 3}) + //. '{"x": 1, "y": 2, "z": 3}' + //. + //. > S.toString(S.Left(S.Right(S.Just(S.Nothing)))) + //. 'Left(Right(Just(Nothing)))' + //. ``` + S.toString = def('toString', {}, [$.Any, $.String], Z.toString); + + //. ### Fantasy Land + //. + //. Sanctuary is compatible with the [Fantasy Land specification][FL]. + + //# equals :: Setoid a => a -> a -> Boolean + //. + //. Curried version of [`Z.equals`][] which requires two arguments of the + //. same type. + //. + //. To compare values of different types first use [`create`](#create) to + //. create a Sanctuary module with type checking disabled, then use that + //. module's `equals` function. + //. + //. ```javascript + //. > S.equals(0, -0) + //. false + //. + //. > S.equals(NaN, NaN) + //. true + //. + //. > S.equals(S.Just([1, 2, 3]), S.Just([1, 2, 3])) + //. true + //. ``` + S.equals = def('equals', {a: [Z.Setoid]}, [a, a, $.Boolean], Z.equals); + + //# concat :: Semigroup a => a -> a -> a + //. + //. Curried version of [`Z.concat`][]. + //. + //. ```javascript + //. > S.concat('abc', 'def') + //. 'abcdef' + //. + //. > S.concat([1, 2, 3], [4, 5, 6]) + //. [1, 2, 3, 4, 5, 6] + //. + //. > S.concat({x: 1, y: 2}, {y: 3, z: 4}) + //. {x: 1, y: 3, z: 4} + //. + //. > S.concat(S.Just([1, 2, 3]), S.Just([4, 5, 6])) + //. Just([1, 2, 3, 4, 5, 6]) + //. ``` + S.concat = def('concat', {a: [Z.Semigroup]}, [a, a, a], Z.concat); + + //# empty :: Monoid a => TypeRep a -> a + //. + //. [Type-safe][sanctuary-def] version of [`Z.empty`][]. + //. + //. ```javascript + //. > S.empty(String) + //. '' + //. + //. > S.empty(Array) + //. [] + //. + //. > S.empty(Object) + //. {} + //. ``` + S.empty = def('empty', {a: [Z.Monoid]}, [TypeRep(a), a], Z.empty); + + //# map :: Functor f => (a -> b) -> f a -> f b + //. + //. Curried version of [`Z.map`][]. + //. + //. ```javascript + //. > S.map(Math.sqrt, [1, 4, 9]) + //. [1, 2, 3] + //. + //. > S.map(Math.sqrt, {x: 1, y: 4, z: 9}) + //. {x: 1, y: 2, z: 3} + //. + //. > S.map(Math.sqrt, s => s.length)('Sanctuary') + //. 3 + //. + //. > S.map(Math.sqrt, S.Just(9)) + //. Just(3) + //. + //. > S.map(Math.sqrt, S.Right(9)) + //. Right(3) + //. ``` + S.map = def('map', {f: [Z.Functor]}, [Fn(a, b), f(a), f(b)], Z.map); + + //# bimap :: Bifunctor f => (a -> b) -> (c -> d) -> f a c -> f b d + //. + //. Curried version of [`Z.bimap`][]. + //. + //. ```javascript + //. > S.bimap(S.toUpper, Math.sqrt, S.Left('foo')) + //. Left('FOO') + //. + //. > S.bimap(S.toUpper, Math.sqrt, S.Right(64)) + //. Right(8) + //. ``` + S.bimap = + def('bimap', + {p: [Z.Bifunctor]}, + [Fn(a, b), Fn(c, d), p(a, c), p(b, d)], + Z.bimap); + + //# promap :: Profunctor p => (a -> b) -> (c -> d) -> p b c -> p a d + //. + //. Curried version of [`Z.promap`][]. + //. + //. ```javascript + //. > S.promap(Math.abs, S.inc, Math.sqrt)(-100) + //. 11 + //. ``` + S.promap = + def('promap', + {p: [Z.Profunctor]}, + [Fn(a, b), Fn(c, d), p(b, c), p(a, d)], + Z.promap); + + //# alt :: Alt f => f a -> f a -> f a + //. + //. Curried version of [`Z.alt`][]. + //. + //. ```javascript + //. > S.alt(S.Nothing, S.Just(1)) + //. Just(1) + //. + //. > S.alt(S.Just(2), S.Just(3)) + //. Just(2) + //. + //. > S.alt(S.Left('X'), S.Right(1)) + //. Right(1) + //. + //. > S.alt(S.Right(2), S.Right(3)) + //. Right(2) + //. ``` + S.alt = def('alt', {f: [Z.Alt]}, [f(a), f(a), f(a)], Z.alt); + + //# zero :: Plus f => TypeRep f -> f a + //. + //. [Type-safe][sanctuary-def] version of [`Z.zero`][]. + //. + //. ```javascript + //. > S.zero(Array) + //. [] + //. + //. > S.zero(Object) + //. {} + //. + //. > S.zero(S.Maybe) + //. Nothing + //. ``` + S.zero = + def('zero', {f: [Z.Plus]}, [TypeRep($.TypeVariable('f')), f(a)], Z.zero); + + //# 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] + //. ``` + function reduce(f, initial, foldable) { + return Z.reduce(uncurry2(f), initial, foldable); + } + S.reduce = + def('reduce', {f: [Z.Foldable]}, [Fn(a, Fn(b, a)), a, f(b), a], reduce); + + //# reduce_ :: Foldable f => ((a, b) -> a) -> a -> f b -> a + //. + //. Variant of [`reduce`](#reduce) which takes an uncurried binary function. + S.reduce_ = + def('reduce_', + {f: [Z.Foldable]}, + [$.Function([a, b, a]), a, f(b), a], + Z.reduce); + + //# traverse :: (Applicative f, Traversable t) => TypeRep f -> (a -> f b) -> t a -> f (t b) + //. + //. Curried version of [`Z.traverse`][]. + //. + //. ```javascript + //. > S.traverse(Array, S.words, S.Just('foo bar baz')) + //. [Just('foo'), Just('bar'), Just('baz')] + //. + //. > S.traverse(Array, S.words, S.Nothing) + //. [Nothing] + //. + //. > S.traverse(S.Maybe, S.parseInt(16), ['A', 'B', 'C']) + //. Just([10, 11, 12]) + //. + //. > S.traverse(S.Maybe, S.parseInt(16), ['A', 'B', 'C', 'X']) + //. Nothing + //. ``` + S.traverse = + def('traverse', + {f: [Z.Applicative], t: [Z.Traversable]}, + [TypeRep($.TypeVariable('f')), Fn(a, f(b)), t(a), f(t(b))], + Z.traverse); + + //# sequence :: (Applicative f, Traversable t) => TypeRep f -> t (f b) -> f (t b) + //. + //. Curried version of [`Z.sequence`][]. + //. + //. ```javascript + //. > S.sequence(Array, S.Just([1, 2, 3])) + //. [Just(1), Just(2), Just(3)] + //. + //. > S.sequence(S.Maybe, [S.Just(1), S.Just(2), S.Just(3)]) + //. Just([1, 2, 3]) + //. + //. > S.sequence(S.Maybe, [S.Just(1), S.Just(2), S.Nothing]) + //. Nothing + //. ``` + S.sequence = + def('sequence', + {f: [Z.Applicative], t: [Z.Traversable]}, + [TypeRep($.TypeVariable('f')), t(f(b)), f(t(b))], + Z.sequence); + + //# ap :: Apply f => f (a -> b) -> f a -> f b + //. + //. Curried version of [`Z.ap`][]. + //. + //. ```javascript + //. > S.ap([Math.sqrt, x => x * x], [1, 4, 9, 16, 25]) + //. [1, 2, 3, 4, 5, 1, 16, 81, 256, 625] + //. + //. > S.ap(s => n => s.slice(0, n), s => Math.ceil(s.length / 2))('Haskell') + //. 'Hask' + //. + //. > S.ap(S.Just(Math.sqrt), S.Just(64)) + //. Just(8) + //. ``` + S.ap = + def('ap', + {f: [Z.Apply]}, + [f(Fn(a, b)), f(a), f(b)], + Z.ap); + + //# apFirst :: Apply f => f a -> f b -> f a + //. + //. Curried version of [`Z.apFirst`][]. Combines two effectful actions, + //. keeping only the result of the first. Equivalent to Haskell's `(<*)` + //. function. + //. + //. See also [`apSecond`](#apSecond). + //. + //. ```javascript + //. > S.apFirst([1, 2], [3, 4]) + //. [1, 1, 2, 2] + //. + //. > S.apFirst(S.Just(1), S.Just(2)) + //. Just(1) + //. ``` + S.apFirst = def('apFirst', {f: [Z.Apply]}, [f(a), f(b), f(a)], Z.apFirst); + + //# apSecond :: Apply f => f a -> f b -> f b + //. + //. Curried version of [`Z.apSecond`][]. Combines two effectful actions, + //. keeping only the result of the second. Equivalent to Haskell's `(*>)` + //. function. + //. + //. See also [`apFirst`](#apFirst). + //. + //. ```javascript + //. > S.apSecond([1, 2], [3, 4]) + //. [3, 4, 3, 4] + //. + //. > S.apSecond(S.Just(1), S.Just(2)) + //. Just(2) + //. ``` + S.apSecond = def('apSecond', {f: [Z.Apply]}, [f(a), f(b), f(b)], Z.apSecond); + + //# of :: Applicative f => TypeRep f -> a -> f a + //. + //. Curried version of [`Z.of`][]. + //. + //. ```javascript + //. > S.of(Array, 42) + //. [42] + //. + //. > S.of(Function, 42)(null) + //. 42 + //. + //. > S.of(S.Maybe, 42) + //. Just(42) + //. + //. > S.of(S.Either, 42) + //. Right(42) + //. ``` + S.of = + def('of', + {f: [Z.Applicative]}, + [TypeRep($.TypeVariable('f')), a, f(a)], + Z.of); + + //# chain :: Chain m => (a -> m b) -> m a -> m b + //. + //. Curried version of [`Z.chain`][]. + //. + //. ```javascript + //. > S.chain(x => [x, x], [1, 2, 3]) + //. [1, 1, 2, 2, 3, 3] + //. + //. > S.chain(n => s => s.slice(0, n), s => Math.ceil(s.length / 2))('slice') + //. 'sli' + //. + //. > S.chain(S.parseInt(10), S.Just('123')) + //. Just(123) + //. + //. > S.chain(S.parseInt(10), S.Just('XXX')) + //. Nothing + //. ``` + S.chain = def('chain', {m: [Z.Chain]}, [Fn(a, m(b)), m(a), m(b)], Z.chain); + + //# join :: Chain m => m (m a) -> m a + //. + //. [Type-safe][sanctuary-def] version of [`Z.join`][]. + //. Removes one level of nesting from a nested monadic structure. + //. + //. ```javascript + //. > S.join([[1], [2], [3]]) + //. [1, 2, 3] + //. + //. > S.join([[[1, 2, 3]]]) + //. [[1, 2, 3]] + //. + //. > S.join(S.concat)('abc') + //. 'abcabc' + //. + //. > S.join(S.Just(S.Just(1))) + //. S.Just(1) + //. ``` + S.join = def('join', {m: [Z.Chain]}, [m(m(a)), m(a)], Z.join); + + //# chainRec :: ChainRec m => TypeRep m -> (a -> m (Either a b)) -> a -> m b + //. + //. Performs a [`chain`](#chain)-like computation with constant stack usage. + //. Similar to [`Z.chainRec`][], but curried and more convenient due to the + //. use of the Either type to indicate completion (via a Right). + //. + //. ```javascript + //. > S.chainRec(Array, + //. . s => s.length === 2 ? S.map(S.Right, [s + '!', s + '?']) + //. . : S.map(S.Left, [s + 'o', s + 'n']), + //. . '') + //. ['oo!', 'oo?', 'on!', 'on?', 'no!', 'no?', 'nn!', 'nn?'] + //. ``` + function chainRec(typeRep, f, x) { + function step(next, done, x) { + return Z.map(function(e) { return either(next, done, e); }, f(x)); + } + return Z.chainRec(typeRep, step, x); + } + S.chainRec = + def('chainRec', + {m: [Z.ChainRec]}, + [TypeRep($.TypeVariable('m')), Fn(a, m($Either(a, b))), a, m(b)], + chainRec); + + //# extend :: Extend w => (w a -> b) -> w a -> w b + //. + //. Curried version of [`Z.extend`][]. + //. + //. ```javascript + //. > S.extend(xs => xs.length, ['foo', 'bar', 'baz', 'quux']) + //. [4] + //. ``` + S.extend = + def('extend', {w: [Z.Extend]}, [Fn(w(a), a), w(a), w(a)], Z.extend); + + //# extract :: Comonad w => w a -> a + //. + //. [Type-safe][sanctuary-def] version of [`Z.extract`][]. + S.extract = + def('extract', {w: [Z.Comonad]}, [w(a), a], Z.extract); + + //# filter :: (Applicative f, Foldable f, Monoid (f a)) => (a -> Boolean) -> f a -> f a + //. + //. Curried version of [`Z.filter`][]. + //. + //. See also [`filterM`](#filterM). + //. + //. ```javascript + //. > S.filter(S.odd, [1, 2, 3, 4, 5]) + //. [1, 3, 5] + //. + //. > S.filter(S.odd, S.Just(9)) + //. Just(9) + //. + //. > S.filter(S.odd, S.Just(4)) + //. Nothing + //. ``` + S.filter = + def('filter', + {f: [Z.Applicative, Z.Foldable, Z.Monoid]}, + [Fn(a, $.Boolean), f(a), f(a)], + Z.filter); + + //# filterM :: (Monad m, Monoid (m a)) => (a -> Boolean) -> m a -> m a + //. + //. Curried version of [`Z.filterM`][]. + //. + //. See also [`filter`](#filter). + //. + //. ```javascript + //. > S.filterM(S.odd, [1, 2, 3, 4, 5]) + //. [1, 3, 5] + //. + //. > S.filterM(S.odd, S.Just(9)) + //. Just(9) + //. + //. > S.filterM(S.odd, S.Just(4)) + //. Nothing + //. ``` + S.filterM = + def('filterM', + {m: [Z.Monad, Z.Monoid]}, + [Fn(a, $.Boolean), m(a), m(a)], + Z.filterM); + //. ### Combinator //# I :: a -> a @@ -527,7 +980,7 @@ //. > S.K('foo', 'bar') //. 'foo' //. - //. > Z.map(S.K(42), S.range(0, 5)) + //. > S.map(S.K(42), S.range(0, 5)) //. [42, 42, 42, 42, 42] //. ``` function K(x, y) { @@ -545,7 +998,7 @@ //. > S.A(S.inc, 42) //. 43 //. - //. > Z.map(S.A(S.__, 100), [S.inc, Math.sqrt]) + //. > S.map(S.A(S.__, 100), [S.inc, Math.sqrt]) //. [101, 10] //. ``` function A(f, x) { @@ -563,7 +1016,7 @@ //. > S.T(42, S.inc) //. 43 //. - //. > Z.map(S.T(100), [S.inc, Math.sqrt]) + //. > S.map(S.T(100), [S.inc, Math.sqrt]) //. [101, 10] //. ``` function T(x, f) { @@ -595,10 +1048,10 @@ //. Curries the given binary function. //. //. ```javascript - //. > Z.map(S.curry2(Math.pow)(10), [1, 2, 3]) + //. > S.map(S.curry2(Math.pow)(10), [1, 2, 3]) //. [10, 100, 1000] //. - //. > Z.map(S.curry2(Math.pow, 10), [1, 2, 3]) + //. > S.map(S.curry2(Math.pow, 10), [1, 2, 3]) //. [10, 100, 1000] //. ``` function curry2(f, x, y) { @@ -713,19 +1166,6 @@ } S.flip_ = def('flip_', {}, [$.Function([a, b, c]), b, a, c], flip_); - //# lift :: Functor f => (a -> b) -> f a -> f b - //. - //. Promotes a unary function to a function which operates on a [Functor][]. - //. - //. ```javascript - //. > S.lift(S.inc, S.Just(2)) - //. Just(3) - //. - //. > S.lift(S.inc, S.Nothing) - //. Nothing - //. ``` - S.lift = def('lift', {f: [Z.Functor]}, [Fn(a, b), f(a), f(b)], Z.map); - //# lift2 :: Apply f => (a -> b -> c) -> f a -> f b -> f c //. //. Promotes a curried binary function to a function which operates on two @@ -886,7 +1326,7 @@ //. Returns Nothing. //. //. ```javascript - //. > Z.empty(S.Maybe) + //. > S.empty(S.Maybe) //. Nothing //. ``` Maybe['fantasy-land/empty'] = function() { return Nothing; }; @@ -896,7 +1336,7 @@ //. Takes a value of any type and returns a Just with the given value. //. //. ```javascript - //. > Z.of(S.Maybe, 42) + //. > S.of(S.Maybe, 42) //. Just(42) //. ``` Maybe['fantasy-land/of'] = Just; @@ -906,7 +1346,7 @@ //. Returns Nothing. //. //. ```javascript - //. > Z.zero(S.Maybe) + //. > S.zero(S.Maybe) //. Nothing //. ``` Maybe['fantasy-land/zero'] = function() { return Nothing; }; @@ -940,10 +1380,10 @@ //. Returns the string representation of the Maybe. //. //. ```javascript - //. > Z.toString(S.Nothing) + //. > S.toString(S.Nothing) //. 'Nothing' //. - //. > Z.toString(S.Just([1, 2, 3])) + //. > S.toString(S.Just([1, 2, 3])) //. 'Just([1, 2, 3])' //. ``` Maybe.prototype.toString = function() { @@ -973,22 +1413,19 @@ //. - it is Nothing and `this` is Nothing; or //. //. - it is a Just and `this` is a Just, and their values are equal - //. according to [`Z.equals`][Z.equals]. + //. according to [`equals`](#equals). //. //. ```javascript - //. > Z.equals(S.Nothing, S.Nothing) + //. > S.equals(S.Nothing, S.Nothing) //. true //. - //. > Z.equals(S.Nothing, null) - //. false - //. - //. > Z.equals(S.Just([1, 2, 3]), S.Just([1, 2, 3])) + //. > S.equals(S.Just([1, 2, 3]), S.Just([1, 2, 3])) //. true //. - //. > Z.equals(S.Just([1, 2, 3]), S.Just([3, 2, 1])) + //. > S.equals(S.Just([1, 2, 3]), S.Just([3, 2, 1])) //. false //. - //. > Z.equals(S.Just([1, 2, 3]), S.Nothing) + //. > S.equals(S.Just([1, 2, 3]), S.Nothing) //. false //. ``` Maybe.prototype['fantasy-land/equals'] = function(other) { @@ -1036,10 +1473,10 @@ //. to this Just's value. //. //. ```javascript - //. > Z.map(Math.sqrt, S.Nothing) + //. > S.map(Math.sqrt, S.Nothing) //. Nothing //. - //. > Z.map(Math.sqrt, S.Just(9)) + //. > S.map(Math.sqrt, S.Just(9)) //. Just(3) //. ``` Maybe.prototype['fantasy-land/map'] = function(f) { @@ -1053,16 +1490,16 @@ //. the result of applying the given Just's value to this Just's value. //. //. ```javascript - //. > Z.ap(S.Nothing, S.Nothing) + //. > S.ap(S.Nothing, S.Nothing) //. Nothing //. - //. > Z.ap(S.Nothing, S.Just(9)) + //. > S.ap(S.Nothing, S.Just(9)) //. Nothing //. - //. > Z.ap(S.Just(Math.sqrt), S.Nothing) + //. > S.ap(S.Just(Math.sqrt), S.Nothing) //. Nothing //. - //. > Z.ap(S.Just(Math.sqrt), S.Just(9)) + //. > S.ap(S.Just(Math.sqrt), S.Just(9)) //. Just(3) //. ``` Maybe.prototype['fantasy-land/ap'] = function(other) { @@ -1075,13 +1512,13 @@ //. it returns the result of applying the function to this Just's value. //. //. ```javascript - //. > Z.chain(S.parseFloat, S.Nothing) + //. > S.chain(S.parseFloat, S.Nothing) //. Nothing //. - //. > Z.chain(S.parseFloat, S.Just('xxx')) + //. > S.chain(S.parseFloat, S.Just('xxx')) //. Nothing //. - //. > Z.chain(S.parseFloat, S.Just('12.34')) + //. > S.chain(S.parseFloat, S.Just('12.34')) //. Just(12.34) //. ``` Maybe.prototype['fantasy-land/chain'] = function(f) { @@ -1094,16 +1531,16 @@ //. Returns `this` if `this` is a Just; the other Maybe otherwise. //. //. ```javascript - //. > Z.alt(S.Nothing, S.Nothing) + //. > S.alt(S.Nothing, S.Nothing) //. Nothing //. - //. > Z.alt(S.Nothing, S.Just(1)) + //. > S.alt(S.Nothing, S.Just(1)) //. Just(1) //. - //. > Z.alt(S.Just(2), S.Nothing) + //. > S.alt(S.Just(2), S.Nothing) //. Just(2) //. - //. > Z.alt(S.Just(3), S.Just(4)) + //. > S.alt(S.Just(3), S.Just(4)) //. Just(3) //. ``` Maybe.prototype['fantasy-land/alt'] = function(other) { @@ -1130,7 +1567,7 @@ return this.isJust ? f(x, this.value) : x; }; - //# Maybe#fantasy-land/traverse :: Applicative f => Maybe a ~> (a -> f b, c -> f c) -> f (Maybe b) + //# Maybe#fantasy-land/traverse :: Applicative f => Maybe a ~> (TypeRep f, a -> f b) -> f (Maybe b) //. //. Takes two functions which both return values of the same [Applicative][], //. (the second of which must be that type's [`of`][] function) and returns: @@ -1141,14 +1578,14 @@ //. first function to this Just's value. //. //. ```javascript - //. > Z.traverse(x => [x], S.words, S.Nothing) + //. > S.traverse(Array, S.words, S.Nothing) //. [Nothing] //. - //. > Z.traverse(x => [x], S.words, S.Just('foo bar baz')) + //. > S.traverse(Array, S.words, S.Just('foo bar baz')) //. [Just('foo'), Just('bar'), Just('baz')] //. ``` - Maybe.prototype['fantasy-land/traverse'] = function(f, of) { - return this.isJust ? Z.map(Just, f(this.value)) : of(this); + Maybe.prototype['fantasy-land/traverse'] = function(typeRep, f) { + return this.isJust ? Z.map(Just, f(this.value)) : Z.of(typeRep, this); }; //# Maybe#fantasy-land/extend :: Maybe a ~> (Maybe a -> b) -> Maybe b @@ -1158,10 +1595,10 @@ //. to `this`. //. //. ```javascript - //. > Z.extend(x => x.value + 1, S.Nothing) + //. > S.extend(x => x.value + 1, S.Nothing) //. Nothing //. - //. > Z.extend(x => x.value + 1, S.Just(42)) + //. > S.extend(x => x.value + 1, S.Just(42)) //. Just(43) //. ``` Maybe.prototype['fantasy-land/extend'] = function(f) { @@ -1514,7 +1951,7 @@ //. Takes a value of any type and returns a Right with the given value. //. //. ```javascript - //. > Z.of(S.Either, 42) + //. > S.of(S.Either, 42) //. Right(42) //. ``` Either['fantasy-land/of'] = Right; @@ -1548,10 +1985,10 @@ //. Returns the string representation of the Either. //. //. ```javascript - //. > Z.toString(S.Left('Cannot divide by zero')) + //. > S.toString(S.Left('Cannot divide by zero')) //. 'Left("Cannot divide by zero")' //. - //. > Z.toString(S.Right([1, 2, 3])) + //. > S.toString(S.Right([1, 2, 3])) //. 'Right([1, 2, 3])' //. ``` Either.prototype.toString = function() { @@ -1580,19 +2017,16 @@ //. Takes a value of the same type and returns `true` if: //. //. - it is a Left and `this` is a Left, and their values are equal - //. according to [`Z.equals`][Z.equals]; or + //. according to [`equals`](#equals); or //. //. - it is a Right and `this` is a Right, and their values are equal - //. according to [`Z.equals`][Z.equals]. + //. according to [`equals`](#equals). //. //. ```javascript - //. > Z.equals(S.Right([1, 2, 3]), S.Right([1, 2, 3])) + //. > S.equals(S.Right([1, 2, 3]), S.Right([1, 2, 3])) //. true //. - //. > Z.equals(S.Right([1, 2, 3]), S.Left([1, 2, 3])) - //. false - //. - //. > Z.equals(S.Right(42), 42) + //. > S.equals(S.Right([1, 2, 3]), S.Left([1, 2, 3])) //. false //. ``` Either.prototype['fantasy-land/equals'] = function(other) { @@ -1642,10 +2076,10 @@ //. See also [`Either#fantasy-land/bimap`][]. //. //. ```javascript - //. > Z.map(Math.sqrt, S.Left('Cannot divide by zero')) + //. > S.map(Math.sqrt, S.Left('Cannot divide by zero')) //. Left('Cannot divide by zero') //. - //. > Z.map(Math.sqrt, S.Right(9)) + //. > S.map(Math.sqrt, S.Right(9)) //. Right(3) //. ``` Either.prototype['fantasy-land/map'] = function(f) { @@ -1666,10 +2100,10 @@ //. left side as well as the right side. //. //. ```javascript - //. > Z.bimap(S.toUpper, S.inc, S.Left('abc')) + //. > S.bimap(S.toUpper, S.inc, S.Left('abc')) //. Left('ABC') //. - //. > Z.bimap(S.toUpper, S.inc, S.Right(42)) + //. > S.bimap(S.toUpper, S.inc, S.Right(42)) //. Right(43) //. ``` Either.prototype['fantasy-land/bimap'] = function(f, g) { @@ -1683,16 +2117,16 @@ //. the result of applying the given Right's value to this Right's value. //. //. ```javascript - //. > Z.ap(S.Left('No such function'), S.Left('Cannot divide by zero')) + //. > S.ap(S.Left('No such function'), S.Left('Cannot divide by zero')) //. Left('No such function') //. - //. > Z.ap(S.Left('No such function'), S.Right(9)) + //. > S.ap(S.Left('No such function'), S.Right(9)) //. Left('No such function') //. - //. > Z.ap(S.Right(Math.sqrt), S.Left('Cannot divide by zero')) + //. > S.ap(S.Right(Math.sqrt), S.Left('Cannot divide by zero')) //. Left('Cannot divide by zero') //. - //. > Z.ap(S.Right(Math.sqrt), S.Right(9)) + //. > S.ap(S.Right(Math.sqrt), S.Right(9)) //. Right(3) //. ``` Either.prototype['fantasy-land/ap'] = function(other) { @@ -1710,13 +2144,13 @@ //. . : S.Right(Math.sqrt(n)) //. sqrt //. - //. > Z.chain(sqrt, S.Left('Cannot divide by zero')) + //. > S.chain(sqrt, S.Left('Cannot divide by zero')) //. Left('Cannot divide by zero') //. - //. > Z.chain(sqrt, S.Right(-1)) + //. > S.chain(sqrt, S.Right(-1)) //. Left('Cannot represent square root of negative number') //. - //. > Z.chain(sqrt, S.Right(25)) + //. > S.chain(sqrt, S.Right(25)) //. Right(5) //. ``` Either.prototype['fantasy-land/chain'] = function(f) { @@ -1729,16 +2163,16 @@ //. Returns `this` if `this` is a Right; the other Either otherwise. //. //. ```javascript - //. > Z.alt(S.Left('A'), S.Left('B')) + //. > S.alt(S.Left('A'), S.Left('B')) //. Left('B') //. - //. > Z.alt(S.Left('C'), S.Right(1)) + //. > S.alt(S.Left('C'), S.Right(1)) //. Right(1) //. - //. > Z.alt(S.Right(2), S.Left('D')) + //. > S.alt(S.Right(2), S.Left('D')) //. Right(2) //. - //. > Z.alt(S.Right(3), S.Right(4)) + //. > S.alt(S.Right(3), S.Right(4)) //. Right(3) //. ``` Either.prototype['fantasy-land/alt'] = function(other) { @@ -1765,7 +2199,7 @@ return this.isRight ? f(x, this.value) : x; }; - //# Either#fantasy-land/traverse :: Applicative f => Either a b ~> (b -> f c, d -> f d) -> f (Either a c) + //# Either#fantasy-land/traverse :: Applicative f => Either a b ~> (TypeRep f, b -> f c) -> f (Either a c) //. //. Takes two functions which both return values of the same [Applicative][], //. (the second of which must be that type's [`of`][] function) and returns: @@ -1776,14 +2210,14 @@ //. the first function to this Right's value. //. //. ```javascript - //. > Z.traverse(x => [x], S.words, S.Left('Request failed')) + //. > S.traverse(Array, S.words, S.Left('Request failed')) //. [Left('Request failed')] //. - //. > Z.traverse(x => [x], S.words, S.Right('foo bar baz')) + //. > S.traverse(Array, S.words, S.Right('foo bar baz')) //. [Right('foo'), Right('bar'), Right('baz')] //. ``` - Either.prototype['fantasy-land/traverse'] = function(f, of) { - return this.isRight ? Z.map(Right, f(this.value)) : of(this); + Either.prototype['fantasy-land/traverse'] = function(typeRep, f) { + return this.isRight ? Z.map(Right, f(this.value)) : Z.of(typeRep, this); }; //# Either#fantasy-land/extend :: Either a b ~> (Either a b -> c) -> Either a c @@ -1793,10 +2227,10 @@ //. `this`. //. //. ```javascript - //. > Z.extend(x => x.value + 1, S.Left('Cannot divide by zero')) + //. > S.extend(x => x.value + 1, S.Left('Cannot divide by zero')) //. Left('Cannot divide by zero') //. - //. > Z.extend(x => x.value + 1, S.Right(42)) + //. > S.extend(x => x.value + 1, S.Right(42)) //. Right(43) //. ``` Either.prototype['fantasy-land/extend'] = function(f) { @@ -1865,10 +2299,10 @@ //. > S.toEither('XYZ', 'ABC') //. Right('ABC') //. - //. > Z.map(S.prop('0'), S.toEither('Invalid protocol', 'ftp://example.com/'.match(/^https?:/))) + //. > S.map(S.prop('0'), S.toEither('Invalid protocol', 'ftp://example.com/'.match(/^https?:/))) //. Left('Invalid protocol') //. - //. > Z.map(S.prop('0'), S.toEither('Invalid protocol', 'https://example.com/'.match(/^https?:/))) + //. > S.map(S.prop('0'), S.toEither('Invalid protocol', 'https://example.com/'.match(/^https?:/))) //. Right('https:') //. ``` function toEither(x, y) { @@ -1884,10 +2318,10 @@ //. Right's value, if the Either is a Right. //. //. ```javascript - //. > S.either(S.toUpper, Z.toString, S.Left('Cannot divide by zero')) + //. > S.either(S.toUpper, S.toString, S.Left('Cannot divide by zero')) //. 'CANNOT DIVIDE BY ZERO' //. - //. > S.either(S.toUpper, Z.toString, S.Right(42)) + //. > S.either(S.toUpper, S.toString, S.Right(42)) //. '42' //. ``` function either(l, r, either) { @@ -2579,8 +3013,8 @@ //# pluck :: Accessible a => String -> Array a -> Array b //. - //. Combines [`map`][Z.map] and [`prop`](#prop). `pluck(k, xs)` is equivalent - //. to `map(prop(k), xs)`. + //. Combines [`map`][`Z.map`] and [`prop`](#prop). `pluck(k, xs)` is + //. equivalent to `map(prop(k), xs)`. //. //. ```javascript //. > S.pluck('x', [{x: 1}, {x: 2}, {x: 3}]) @@ -2600,40 +3034,6 @@ [$.String, $.Array(a), $.Array(b)], pluck); - //# 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] - //. ``` - function reduce(f, initial, foldable) { - return Z.reduce(uncurry2(f), initial, foldable); - } - S.reduce = - def('reduce', {f: [Z.Foldable]}, [Fn(a, Fn(b, a)), a, f(b), a], reduce); - - //# reduce_ :: Foldable f => ((a, b) -> a) -> a -> f b -> a - //. - //. Variant of [`reduce`](#reduce) which takes an uncurried binary function. - S.reduce_ = - def('reduce_', - {f: [Z.Foldable]}, - [$.Function([a, b, a]), a, f(b), a], - Z.reduce); - //# unfoldr :: (b -> Maybe (Pair a b)) -> b -> Array a //. //. Takes a function and a seed value, and returns an array generated by @@ -3499,6 +3899,7 @@ //. [Bifunctor]: v:fantasyland/fantasy-land#bifunctor //. [BinaryType]: v:sanctuary-js/sanctuary-def#BinaryType //. [Extend]: v:fantasyland/fantasy-land#extend +//. [FL]: v:fantasyland/fantasy-land //. [Foldable]: v:fantasyland/fantasy-land#foldable //. [Functor]: v:fantasyland/fantasy-land#functor //. [Monad]: v:fantasyland/fantasy-land#monad @@ -3511,8 +3912,28 @@ //. [Setoid]: v:fantasyland/fantasy-land#setoid //. [Traversable]: v:fantasyland/fantasy-land#traversable //. [UnaryType]: v:sanctuary-js/sanctuary-def#UnaryType -//. [Z.equals]: v:sanctuary-js/sanctuary-type-classes#equals -//. [Z.map]: v:sanctuary-js/sanctuary-type-classes#map +//. [`Z.alt`]: v:sanctuary-js/sanctuary-type-classes#alt +//. [`Z.ap`]: v:sanctuary-js/sanctuary-type-classes#ap +//. [`Z.apFirst`]: v:sanctuary-js/sanctuary-type-classes#apFirst +//. [`Z.apSecond`]: v:sanctuary-js/sanctuary-type-classes#apSecond +//. [`Z.bimap`]: v:sanctuary-js/sanctuary-type-classes#bimap +//. [`Z.chainRec`]: v:sanctuary-js/sanctuary-type-classes#chainRec +//. [`Z.chain`]: v:sanctuary-js/sanctuary-type-classes#chain +//. [`Z.concat`]: v:sanctuary-js/sanctuary-type-classes#concat +//. [`Z.empty`]: v:sanctuary-js/sanctuary-type-classes#empty +//. [`Z.equals`]: v:sanctuary-js/sanctuary-type-classes#equals +//. [`Z.extend`]: v:sanctuary-js/sanctuary-type-classes#extend +//. [`Z.extract`]: v:sanctuary-js/sanctuary-type-classes#extract +//. [`Z.filterM`]: v:sanctuary-js/sanctuary-type-classes#filterM +//. [`Z.filter`]: v:sanctuary-js/sanctuary-type-classes#filter +//. [`Z.join`]: v:sanctuary-js/sanctuary-type-classes#join +//. [`Z.map`]: v:sanctuary-js/sanctuary-type-classes#map +//. [`Z.of`]: v:sanctuary-js/sanctuary-type-classes#of +//. [`Z.promap`]: v:sanctuary-js/sanctuary-type-classes#promap +//. [`Z.sequence`]: v:sanctuary-js/sanctuary-type-classes#sequence +//. [`Z.toString`]: v:sanctuary-js/sanctuary-type-classes#toString +//. [`Z.traverse`]: v:sanctuary-js/sanctuary-type-classes#traverse +//. [`Z.zero`]: v:sanctuary-js/sanctuary-type-classes#zero //. [`of`]: v:fantasyland/fantasy-land#of-method //. [parseInt]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt //. [sanctuary-def]: v:sanctuary-js/sanctuary-def diff --git a/package.json b/package.json index 88104e11..8d6b0b67 100644 --- a/package.json +++ b/package.json @@ -12,14 +12,14 @@ }, "dependencies": { "sanctuary-def": "0.9.0", - "sanctuary-type-classes": "2.0.1", + "sanctuary-type-classes": "3.0.0", "sanctuary-type-identifiers": "1.0.0" }, "devDependencies": { "browserify": "13.1.x", "doctest": "0.11.x", "eslint": "2.9.x", - "fantasy-land": "2.2.0", + "fantasy-land": "3.0.0", "istanbul": "0.4.x", "jsverify": "0.7.x", "karma": "1.3.x", diff --git a/test/Either/Either.js b/test/Either/Either.js index 5a3d114c..2f0c6cec 100644 --- a/test/Either/Either.js +++ b/test/Either/Either.js @@ -7,6 +7,7 @@ var S = require('../internal/sanctuary'); var EitherArb = require('../internal/EitherArb'); var Identity = require('../internal/Identity'); var IdentityArb = require('../internal/IdentityArb'); +var add_ = require('../internal/add_'); var equals = require('../internal/equals'); var laws = require('../internal/laws'); var squareRoot = require('../internal/squareRoot'); @@ -163,7 +164,7 @@ suite('Either', function() { var foldableLaws = laws.Foldable(equals); foldableLaws.associativity( - jsc.constant(function(x, y) { return x + y; }), + jsc.constant(add_), jsc.number, EitherArb(jsc.string, jsc.number) ); diff --git a/test/Maybe/Maybe.js b/test/Maybe/Maybe.js index c74809b1..dd9e66e9 100644 --- a/test/Maybe/Maybe.js +++ b/test/Maybe/Maybe.js @@ -8,6 +8,7 @@ var EitherArb = require('../internal/EitherArb'); var Identity = require('../internal/Identity'); var IdentityArb = require('../internal/IdentityArb'); var MaybeArb = require('../internal/MaybeArb'); +var add_ = require('../internal/add_'); var equals = require('../internal/equals'); var laws = require('../internal/laws'); @@ -193,7 +194,7 @@ suite('Maybe', function() { var foldableLaws = laws.Foldable(equals); foldableLaws.associativity( - jsc.constant(function(x, y) { return x + y; }), + jsc.constant(add_), jsc.number, MaybeArb(jsc.number) ); diff --git a/test/alt.js b/test/alt.js new file mode 100644 index 00000000..0649551f --- /dev/null +++ b/test/alt.js @@ -0,0 +1,28 @@ +'use strict'; + +var S = require('..'); + +var eq = require('./internal/eq'); + + +test('alt', function() { + + eq(typeof S.alt, 'function'); + eq(S.alt.length, 2); + eq(S.alt.toString(), 'alt :: Alt f => f a -> f a -> f a'); + + eq(S.alt([], []), []); + eq(S.alt([], [1, 2, 3]), [1, 2, 3]); + eq(S.alt([1, 2, 3], []), [1, 2, 3]); + eq(S.alt([1, 2, 3], [4, 5, 6]), [1, 2, 3, 4, 5, 6]); + eq(S.alt({}, {}), {}); + eq(S.alt({}, {a: 1, b: 2, c: 3}), {a: 1, b: 2, c: 3}); + eq(S.alt({a: 1, b: 2, c: 3}, {}), {a: 1, b: 2, c: 3}); + eq(S.alt({a: 1, b: 2, c: 3}, {d: 4, e: 5, f: 6}), {a: 1, b: 2, c: 3, d: 4, e: 5, f: 6}); + eq(S.alt({a: 1, b: 2, c: 3}, {c: 4, d: 5, e: 6}), {a: 1, b: 2, c: 4, d: 5, e: 6}); + eq(S.alt(S.Nothing, S.Nothing), S.Nothing); + eq(S.alt(S.Nothing, S.Just(1)), S.Just(1)); + eq(S.alt(S.Just(2), S.Nothing), S.Just(2)); + eq(S.alt(S.Just(3), S.Just(4)), S.Just(3)); + +}); diff --git a/test/ap.js b/test/ap.js new file mode 100644 index 00000000..73d04cef --- /dev/null +++ b/test/ap.js @@ -0,0 +1,24 @@ +'use strict'; + +var S = require('..'); + +var eq = require('./internal/eq'); + + +test('ap', function() { + + eq(typeof S.ap, 'function'); + eq(S.ap.length, 2); + eq(S.ap.toString(), 'ap :: Apply f => f (a -> b) -> f a -> f b'); + + eq(S.ap([], []), []); + eq(S.ap([], [1, 2, 3]), []); + eq(S.ap([S.inc], []), []); + eq(S.ap([S.inc], [1, 2, 3]), [2, 3, 4]); + eq(S.ap([S.dec, Math.sqrt], [1, 4, 9]), [0, 3, 8, 1, 2, 3]); + eq(S.ap(S.Nothing, S.Nothing), S.Nothing); + eq(S.ap(S.Nothing, S.Just(9)), S.Nothing); + eq(S.ap(S.Just(Math.sqrt), S.Nothing), S.Nothing); + eq(S.ap(S.Just(Math.sqrt), S.Just(9)), S.Just(3)); + +}); diff --git a/test/apFirst.js b/test/apFirst.js new file mode 100644 index 00000000..c5f3e68b --- /dev/null +++ b/test/apFirst.js @@ -0,0 +1,17 @@ +'use strict'; + +var S = require('..'); + +var eq = require('./internal/eq'); + + +test('apFirst', function() { + + eq(typeof S.apFirst, 'function'); + eq(S.apFirst.length, 2); + eq(S.apFirst.toString(), 'apFirst :: Apply f => f a -> f b -> f a'); + + eq(S.apFirst([1, 2], [3, 4]), [1, 1, 2, 2]); + eq(S.apFirst(S.Just(1), S.Just(2)), S.Just(1)); + +}); diff --git a/test/apSecond.js b/test/apSecond.js new file mode 100644 index 00000000..38c6d3ef --- /dev/null +++ b/test/apSecond.js @@ -0,0 +1,17 @@ +'use strict'; + +var S = require('..'); + +var eq = require('./internal/eq'); + + +test('apSecond', function() { + + eq(typeof S.apSecond, 'function'); + eq(S.apSecond.length, 2); + eq(S.apSecond.toString(), 'apSecond :: Apply f => f a -> f b -> f b'); + + eq(S.apSecond([1, 2], [3, 4]), [3, 4, 3, 4]); + eq(S.apSecond(S.Just(1), S.Just(2)), S.Just(2)); + +}); diff --git a/test/bimap.js b/test/bimap.js new file mode 100644 index 00000000..f032e709 --- /dev/null +++ b/test/bimap.js @@ -0,0 +1,17 @@ +'use strict'; + +var S = require('..'); + +var eq = require('./internal/eq'); + + +test('bimap', function() { + + eq(typeof S.bimap, 'function'); + eq(S.bimap.length, 3); + eq(S.bimap.toString(), 'bimap :: Bifunctor p => (a -> b) -> (c -> d) -> p a c -> p b d'); + + eq(S.bimap(S.toUpper, S.inc, S.Left('xxx')), S.Left('XXX')); + eq(S.bimap(S.toUpper, S.inc, S.Right(1000)), S.Right(1001)); + +}); diff --git a/test/chain.js b/test/chain.js new file mode 100644 index 00000000..7ce874a1 --- /dev/null +++ b/test/chain.js @@ -0,0 +1,19 @@ +'use strict'; + +var S = require('..'); + +var eq = require('./internal/eq'); + + +test('chain', function() { + + eq(typeof S.chain, 'function'); + eq(S.chain.length, 2); + eq(S.chain.toString(), 'chain :: Chain m => (a -> m b) -> m a -> m b'); + + eq(S.chain(S.I, [[1, 2], [3, 4], [5, 6]]), [1, 2, 3, 4, 5, 6]); + eq(S.chain(S.parseFloat, S.Nothing), S.Nothing); + eq(S.chain(S.parseFloat, S.Just('X')), S.Nothing); + eq(S.chain(S.parseFloat, S.Just('0')), S.Just(0)); + +}); diff --git a/test/chainRec.js b/test/chainRec.js new file mode 100644 index 00000000..0759fd6d --- /dev/null +++ b/test/chainRec.js @@ -0,0 +1,29 @@ +'use strict'; + +var S = require('..'); + +var eq = require('./internal/eq'); +var map = require('./internal/map'); + + +test('chainRec', function() { + + eq(typeof S.chainRec, 'function'); + eq(S.chainRec.length, 3); + eq(S.chainRec.toString(), 'chainRec :: ChainRec m => TypeRep m -> (a -> m (Either a b)) -> a -> m b'); + + function permute(s) { + return s.length === 2 ? map(S.Right)([s + '!', s + '?']) + : map(S.Left)([s + 'o', s + 'n']); + } + + eq(S.chainRec(Array, permute, ''), ['oo!', 'oo?', 'on!', 'on?', 'no!', 'no?', 'nn!', 'nn?']); + + function stepper(n) { + return n === 3000 ? map(S.Right)(function(env) { return n + env.inc; }) + : map(S.Left)(function(env) { return n + env.step; }); + } + + eq(S.chainRec(Function, stepper, 0)({step: 2, inc: 100}), 3100); + +}); diff --git a/test/curry2.js b/test/curry2.js index 524fd06a..fb6b2fa7 100644 --- a/test/curry2.js +++ b/test/curry2.js @@ -2,6 +2,7 @@ var S = require('..'); +var add_ = require('./internal/add_'); var eq = require('./internal/eq'); @@ -11,7 +12,7 @@ test('curry2', function() { eq(S.curry2.length, 3); eq(S.curry2.toString(), 'curry2 :: ((a, b) -> c) -> a -> b -> c'); - var curried = S.curry2(function(x, y) { return x + y; }); + var curried = S.curry2(add_); eq(curried(1, 2), 3); eq(curried(1)(2), 3); diff --git a/test/empty.js b/test/empty.js new file mode 100644 index 00000000..e18a7e0e --- /dev/null +++ b/test/empty.js @@ -0,0 +1,18 @@ +'use strict'; + +var S = require('..'); + +var eq = require('./internal/eq'); + + +test('empty', function() { + + eq(typeof S.empty, 'function'); + eq(S.empty.length, 1); + eq(S.empty.toString(), 'empty :: Monoid a => TypeRep a -> a'); + + eq(S.empty(String), ''); + eq(S.empty(Array), []); + eq(S.empty(Object), {}); + +}); diff --git a/test/equals.js b/test/equals.js new file mode 100644 index 00000000..81f6ce9f --- /dev/null +++ b/test/equals.js @@ -0,0 +1,22 @@ +'use strict'; + +var S = require('..'); + +var eq = require('./internal/eq'); + + +test('equals', function() { + + eq(typeof S.equals, 'function'); + eq(S.equals.length, 2); + eq(S.equals.toString(), 'equals :: Setoid a => a -> a -> Boolean'); + + eq(S.equals(S.Nothing, S.Nothing), true); + eq(S.equals(S.Just(NaN), S.Just(NaN)), true); + eq(S.equals(S.Just(0), S.Just(-0)), false); + eq(S.equals(S.Left(NaN), S.Left(NaN)), true); + eq(S.equals(S.Left(0), S.Left(-0)), false); + eq(S.equals(S.Right(NaN), S.Right(NaN)), true); + eq(S.equals(S.Right(0), S.Right(-0)), false); + +}); diff --git a/test/extend.js b/test/extend.js new file mode 100644 index 00000000..5c68d5bd --- /dev/null +++ b/test/extend.js @@ -0,0 +1,19 @@ +'use strict'; + +var S = require('./internal/sanctuary'); + +var Identity = require('./internal/Identity'); +var eq = require('./internal/eq'); + + +test('extend', function() { + + eq(typeof S.extend, 'function'); + eq(S.extend.length, 2); + eq(S.extend.toString(), 'extend :: Extend w => (w a -> a) -> w a -> w a'); + + eq(S.extend(S.prop('length'), []), [0]); + eq(S.extend(S.prop('length'), [1, 2, 3]), [3]); + eq(S.extend(S.reduce(S.add, 1), Identity(42)), Identity(43)); + +}); diff --git a/test/extract.js b/test/extract.js new file mode 100644 index 00000000..00252651 --- /dev/null +++ b/test/extract.js @@ -0,0 +1,17 @@ +'use strict'; + +var S = require('./internal/sanctuary'); + +var Identity = require('./internal/Identity'); +var eq = require('./internal/eq'); + + +test('extract', function() { + + eq(typeof S.extract, 'function'); + eq(S.extract.length, 1); + eq(S.extract.toString(), 'extract :: Comonad w => w a -> a'); + + eq(S.extract(Identity(42)), 42); + +}); diff --git a/test/filter.js b/test/filter.js new file mode 100644 index 00000000..7af75241 --- /dev/null +++ b/test/filter.js @@ -0,0 +1,22 @@ +'use strict'; + +var S = require('..'); + +var eq = require('./internal/eq'); + + +test('filter', function() { + + eq(typeof S.filter, 'function'); + eq(S.filter.length, 2); + eq(S.filter.toString(), 'filter :: (Applicative f, Foldable f, Monoid f) => (a -> Boolean) -> f a -> f a'); + + eq(S.filter(S.odd, []), []); + eq(S.filter(S.odd, [0, 2, 4, 6, 8]), []); + eq(S.filter(S.odd, [1, 3, 5, 7, 9]), [1, 3, 5, 7, 9]); + eq(S.filter(S.odd, [1, 2, 3, 4, 5]), [1, 3, 5]); + eq(S.filter(S.odd, S.Nothing), S.Nothing); + eq(S.filter(S.odd, S.Just(4)), S.Nothing); + eq(S.filter(S.odd, S.Just(9)), S.Just(9)); + +}); diff --git a/test/filterM.js b/test/filterM.js new file mode 100644 index 00000000..c6e7381d --- /dev/null +++ b/test/filterM.js @@ -0,0 +1,22 @@ +'use strict'; + +var S = require('..'); + +var eq = require('./internal/eq'); + + +test('filterM', function() { + + eq(typeof S.filterM, 'function'); + eq(S.filterM.length, 2); + eq(S.filterM.toString(), 'filterM :: (Monad m, Monoid m) => (a -> Boolean) -> m a -> m a'); + + eq(S.filterM(S.odd, []), []); + eq(S.filterM(S.odd, [0, 2, 4, 6, 8]), []); + eq(S.filterM(S.odd, [1, 3, 5, 7, 9]), [1, 3, 5, 7, 9]); + eq(S.filterM(S.odd, [1, 2, 3, 4, 5]), [1, 3, 5]); + eq(S.filterM(S.odd, S.Nothing), S.Nothing); + eq(S.filterM(S.odd, S.Just(4)), S.Nothing); + eq(S.filterM(S.odd, S.Just(9)), S.Just(9)); + +}); diff --git a/test/internal/Compose.js b/test/internal/Compose.js index 3890ebfc..0b40b850 100644 --- a/test/internal/Compose.js +++ b/test/internal/Compose.js @@ -9,7 +9,7 @@ var of = require('./of'); var toString = require('./toString'); -// Compose :: (Apply f, Apply g) => { of :: b -> f b } -> { of :: c -> g c } -> f (g a) -> Compose f g a +// Compose :: (Apply f, Apply g) => TypeRep f -> TypeRep g -> f (g a) -> Compose f g a module.exports = function Compose(F) { return function ComposeF(G) { function ComposeFG(value) { diff --git a/test/internal/Identity.js b/test/internal/Identity.js index 85d254cb..84d08227 100644 --- a/test/internal/Identity.js +++ b/test/internal/Identity.js @@ -42,7 +42,7 @@ Identity.prototype[FL.reduce] = function(f, x) { return f(x, this.value); }; -Identity.prototype[FL.traverse] = function(f, of) { +Identity.prototype[FL.traverse] = function(typeRep, f) { return map(Identity)(f(this.value)); }; diff --git a/test/internal/add_.js b/test/internal/add_.js new file mode 100644 index 00000000..ac8737bf --- /dev/null +++ b/test/internal/add_.js @@ -0,0 +1,4 @@ +'use strict'; + +// add_ :: (Number, Number) -> Number +module.exports = function add_(a, b) { return a + b; }; diff --git a/test/internal/laws/Traversable.js b/test/internal/laws/Traversable.js index 06e8f5d4..b3c9585e 100644 --- a/test/internal/laws/Traversable.js +++ b/test/internal/laws/Traversable.js @@ -11,25 +11,25 @@ var traverse = require('../traverse'); module.exports = function(equals) { return forall({ - // t (traverse (of F) id u) = traverse (of G) t u + // t (traverse F id u) = traverse G t u naturality: function(t, u, F, G) { - var lhs = t(traverse(of(F))(id)(u)); - var rhs = traverse(of(G))(t)(u); + var lhs = t(traverse(F)(id)(u)); + var rhs = traverse(G)(t)(u); return equals(lhs)(rhs); }, - // traverse (of F) (of F) u = of F u + // traverse F F u = of F u identity: function(u, F) { - var lhs = traverse(of(F))(of(F))(u); + var lhs = traverse(F)(F)(u); var rhs = of(F)(u); return equals(lhs)(rhs); }, - // traverse (of C) C u = C (traverse (of G) id <$> traverse (of F) id u) + // traverse C C u = C (traverse G id <$> traverse F id u) composition: function(u, F, G) { var C = Compose(F)(G); - var lhs = traverse(of(C))(C)(u); - var rhs = C(map(traverse(of(G))(id))(traverse(of(F))(id)(u))); + var lhs = traverse(C)(C)(u); + var rhs = C(map(traverse(G)(id))(traverse(F)(id)(u))); return equals(lhs)(rhs); } diff --git a/test/internal/traverse.js b/test/internal/traverse.js index c2aef86e..6d45e664 100644 --- a/test/internal/traverse.js +++ b/test/internal/traverse.js @@ -4,5 +4,5 @@ var Z = require('sanctuary-type-classes'); var curry3 = require('./curry3'); -// traverse :: (Applicative f, Traversable t) => (a -> f a) -> (b -> f c) -> t b -> f (t c) +// traverse :: (Applicative f, Traversable t) => TypeRep f -> (a -> f b) -> t a -> f (t b) module.exports = curry3(Z.traverse); diff --git a/test/join.js b/test/join.js new file mode 100644 index 00000000..52e31c85 --- /dev/null +++ b/test/join.js @@ -0,0 +1,24 @@ +'use strict'; + +var S = require('..'); + +var eq = require('./internal/eq'); + + +test('join', function() { + + eq(typeof S.join, 'function'); + eq(S.join.length, 1); + eq(S.join.toString(), 'join :: Chain m => m (m a) -> m a'); + + eq(S.join([]), []); + eq(S.join([[]]), []); + eq(S.join([[[]]]), [[]]); + eq(S.join([[1], [2], [3]]), [1, 2, 3]); + eq(S.join([[[1, 2, 3]]]), [[1, 2, 3]]); + eq(S.join(S.Nothing), S.Nothing); + eq(S.join(S.Just(S.Nothing)), S.Nothing); + eq(S.join(S.Just(S.Just(1))), S.Just(1)); + eq(S.join(S.Just(S.Just(S.Just(1)))), S.Just(S.Just(1))); + +}); diff --git a/test/lift.js b/test/lift.js deleted file mode 100644 index 52ee5a3d..00000000 --- a/test/lift.js +++ /dev/null @@ -1,26 +0,0 @@ -'use strict'; - -var S = require('..'); - -var eq = require('./internal/eq'); - - -test('lift', function() { - - eq(typeof S.lift, 'function'); - eq(S.lift.length, 2); - eq(S.lift.toString(), 'lift :: Functor f => (a -> b) -> f a -> f b'); - - eq(S.lift(S.mult(2), S.Just(3)), S.Just(6)); - eq(S.lift(S.mult(2), S.Nothing), S.Nothing); - - eq(S.lift(S.mult(2), S.Left(3)), S.Left(3)); - eq(S.lift(S.mult(2), S.Right(3)), S.Right(6)); - - eq(S.lift(S.mult(2), [1, 2, 3]), [2, 4, 6]); - eq(S.lift(S.mult(2), []), []); - - eq(S.lift(S.not, S.even)(42), false); - eq(S.lift(S.not, S.even)(43), true); - -}); diff --git a/test/map.js b/test/map.js new file mode 100644 index 00000000..d3e8483a --- /dev/null +++ b/test/map.js @@ -0,0 +1,29 @@ +'use strict'; + +var S = require('..'); + +var eq = require('./internal/eq'); + + +test('map', function() { + + eq(typeof S.map, 'function'); + eq(S.map.length, 2); + eq(S.map.toString(), 'map :: Functor f => (a -> b) -> f a -> f b'); + + 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), {}), {}); + +}); diff --git a/test/of.js b/test/of.js new file mode 100644 index 00000000..46671a0e --- /dev/null +++ b/test/of.js @@ -0,0 +1,21 @@ +'use strict'; + +var S = require('./internal/sanctuary'); + +var Identity = require('./internal/Identity'); +var eq = require('./internal/eq'); + + +test('of', function() { + + eq(typeof S.of, 'function'); + eq(S.of.length, 2); + eq(S.of.toString(), 'of :: Applicative f => TypeRep f -> a -> f a'); + + eq(S.of(Array, 42), [42]); + eq(S.of(Function, 42)(null), 42); + eq(S.of(S.Maybe, 42), S.Just(42)); + eq(S.of(S.Either, 42), S.Right(42)); + eq(S.of(Identity, 42), Identity(42)); + +}); diff --git a/test/promap.js b/test/promap.js new file mode 100644 index 00000000..ad23010c --- /dev/null +++ b/test/promap.js @@ -0,0 +1,20 @@ +'use strict'; + +var S = require('..'); + +var eq = require('./internal/eq'); + + +test('promap', function() { + + eq(typeof S.promap, 'function'); + eq(S.promap.length, 3); + eq(S.promap.toString(), 'promap :: Profunctor p => (a -> b) -> (c -> d) -> p b c -> p a d'); + + var before = S.map(S.prop('length')); + var after = S.flip_(Math.pow)(2); + eq(S.promap(before, after, S.sum)(['foo', 'bar', 'baz', 'quux']), 169); + + eq(S.promap(Math.abs, S.inc, Math.sqrt)(-100), 11); + +}); diff --git a/test/reduce_.js b/test/reduce_.js index f350fe37..cc73d622 100644 --- a/test/reduce_.js +++ b/test/reduce_.js @@ -2,6 +2,7 @@ var S = require('..'); +var add_ = require('./internal/add_'); var eq = require('./internal/eq'); @@ -11,8 +12,8 @@ test('reduce_', function() { eq(S.reduce_.length, 3); eq(S.reduce_.toString(), 'reduce_ :: Foldable f => ((a, b) -> a) -> a -> f b -> a'); - eq(S.reduce_(function(a, b) { return a + b; }, 0, []), 0); - eq(S.reduce_(function(a, b) { return a + b; }, 0, [1, 2, 3, 4, 5]), 15); - eq(S.reduce_(function(a, b) { return a + b; }, 10, S.Just(5)), 15); + eq(S.reduce_(add_, 0, []), 0); + eq(S.reduce_(add_, 0, [1, 2, 3, 4, 5]), 15); + eq(S.reduce_(add_, 10, S.Just(5)), 15); }); diff --git a/test/sequence.js b/test/sequence.js new file mode 100644 index 00000000..3d0e4e82 --- /dev/null +++ b/test/sequence.js @@ -0,0 +1,29 @@ +'use strict'; + +var S = require('./internal/sanctuary'); + +var Identity = require('./internal/Identity'); +var eq = require('./internal/eq'); + + +test('sequence', function() { + + eq(typeof S.sequence, 'function'); + eq(S.sequence.length, 2); + eq(S.sequence.toString(), 'sequence :: (Applicative f, Traversable t) => TypeRep f -> t (f b) -> f (t b)'); + + eq(S.sequence(Identity, []), Identity([])); + eq(S.sequence(Identity, [Identity(1), Identity(2), Identity(3)]), Identity([1, 2, 3])); + eq(S.sequence(Identity, S.Nothing), Identity(S.Nothing)); + eq(S.sequence(Identity, S.Just(Identity(0))), Identity(S.Just(0))); + eq(S.sequence(Identity, S.Left('A')), Identity(S.Left('A'))); + eq(S.sequence(Identity, S.Right(Identity(-1))), Identity(S.Right(-1))); + + eq(S.sequence(Array, Identity([])), []); + eq(S.sequence(Array, Identity([1, 2, 3])), [Identity(1), Identity(2), Identity(3)]); + eq(S.sequence(S.Maybe, Identity(S.Nothing)), S.Nothing); + eq(S.sequence(S.Maybe, Identity(S.Just(0))), S.Just(Identity(0))); + eq(S.sequence(S.Either, Identity(S.Left('A'))), S.Left('A')); + eq(S.sequence(S.Either, Identity(S.Right(-1))), S.Right(Identity(-1))); + +}); diff --git a/test/toString.js b/test/toString.js new file mode 100644 index 00000000..3ce134de --- /dev/null +++ b/test/toString.js @@ -0,0 +1,57 @@ +'use strict'; + +var S = require('..'); + +var eq = require('./internal/eq'); + + +test('toString', function() { + + eq(typeof S.toString, 'function'); + eq(S.toString.length, 1); + eq(S.toString.toString(), 'toString :: Any -> String'); + + eq(S.toString(null), 'null'); + eq(S.toString(undefined), 'undefined'); + eq(S.toString(false), 'false'); + eq(S.toString(true), 'true'); + eq(S.toString(new Boolean(false)), 'new Boolean(false)'); + eq(S.toString(new Boolean(true)), 'new Boolean(true)'); + eq(S.toString(0), '0'); + eq(S.toString(-0), '-0'); + eq(S.toString(NaN), 'NaN'); + eq(S.toString(Math.PI), '3.141592653589793'); + eq(S.toString(-Math.PI), '-3.141592653589793'); + eq(S.toString(Infinity), 'Infinity'); + eq(S.toString(-Infinity), '-Infinity'); + eq(S.toString(new Number(0)), 'new Number(0)'); + eq(S.toString(new Number(-0)), 'new Number(-0)'); + eq(S.toString(new Number(NaN)), 'new Number(NaN)'); + eq(S.toString(new Number(Math.PI)), 'new Number(3.141592653589793)'); + eq(S.toString(new Number(-Math.PI)), 'new Number(-3.141592653589793)'); + eq(S.toString(new Number(Infinity)), 'new Number(Infinity)'); + eq(S.toString(new Number(-Infinity)), 'new Number(-Infinity)'); + eq(S.toString(''), '""'); + eq(S.toString('foo'), '"foo"'); + eq(S.toString('foo "bar" baz'), '"foo \\"bar\\" baz"'); + eq(S.toString(new String('')), 'new String("")'); + eq(S.toString(new String('foo')), 'new String("foo")'); + eq(S.toString(new String('foo "bar" baz')), 'new String("foo \\"bar\\" baz")'); + eq(S.toString(new Date(0)), 'new Date("1970-01-01T00:00:00.000Z")'); + eq(S.toString(new Date(42)), 'new Date("1970-01-01T00:00:00.042Z")'); + eq(S.toString(new Date(NaN)), 'new Date(NaN)'); + eq(S.toString(new Date('2001-02-03T04:05:06')), 'new Date("2001-02-03T04:05:06.000Z")'); + eq(S.toString([]), '[]'); + eq(S.toString(['foo']), '["foo"]'); + eq(S.toString(['foo', 'bar', 'baz']), '["foo", "bar", "baz"]'); + eq(S.toString(['foo "bar" baz']), '["foo \\"bar\\" baz"]'); + eq(S.toString({}), '{}'); + eq(S.toString({x: 1}), '{"x": 1}'); + eq(S.toString({x: 1, y: 2, z: 3}), '{"x": 1, "y": 2, "z": 3}'); + eq(S.toString({'foo "bar" baz': '"quux"'}), '{"foo \\"bar\\" baz": "\\"quux\\""}'); + eq(S.toString(S.Nothing), 'Nothing'); + eq(S.toString(S.Just(9)), 'Just(9)'); + eq(S.toString(S.Left(false)), 'Left(false)'); + eq(S.toString(S.Right(true)), 'Right(true)'); + +}); diff --git a/test/traverse.js b/test/traverse.js new file mode 100644 index 00000000..ad83bbd5 --- /dev/null +++ b/test/traverse.js @@ -0,0 +1,33 @@ +'use strict'; + +var S = require('./internal/sanctuary'); + +var Identity = require('./internal/Identity'); +var eq = require('./internal/eq'); + + +test('traverse', function() { + + eq(typeof S.traverse, 'function'); + eq(S.traverse.length, 3); + eq(S.traverse.toString(), 'traverse :: (Applicative f, Traversable t) => TypeRep f -> (a -> f b) -> t a -> f (t b)'); + + eq(S.traverse(S.Maybe, S.parseInt(16), ['A', 'B', 'C']), S.Just([10, 11, 12])); + eq(S.traverse(S.Maybe, S.parseInt(16), ['A', 'B', 'C', 'X']), S.Nothing); + + eq(S.traverse(Array, S.I, []), [[]]); + eq(S.traverse(Array, S.I, [['A', 'a']]), [['A'], ['a']]); + eq(S.traverse(Array, S.I, [['A', 'a'], ['B']]), [['A', 'B'], ['a', 'B']]); + eq(S.traverse(Array, S.I, [['A', 'a'], ['B', 'b']]), [['A', 'B'], ['A', 'b'], ['a', 'B'], ['a', 'b']]); + + eq(S.traverse(Array, S.words, Identity('')), []); + eq(S.traverse(Array, S.words, Identity('foo')), [Identity('foo')]); + eq(S.traverse(Array, S.words, Identity('foo bar')), [Identity('foo'), Identity('bar')]); + eq(S.traverse(Array, S.words, Identity('foo bar baz')), [Identity('foo'), Identity('bar'), Identity('baz')]); + + eq(S.traverse(Identity, S.I, []), Identity([])); + eq(S.traverse(Identity, S.I, [Identity(1)]), Identity([1])); + eq(S.traverse(Identity, S.I, [Identity(1), Identity(2)]), Identity([1, 2])); + eq(S.traverse(Identity, S.I, [Identity(1), Identity(2), Identity(3)]), Identity([1, 2, 3])); + +}); diff --git a/test/zero.js b/test/zero.js new file mode 100644 index 00000000..b0407c47 --- /dev/null +++ b/test/zero.js @@ -0,0 +1,18 @@ +'use strict'; + +var S = require('..'); + +var eq = require('./internal/eq'); + + +test('zero', function() { + + eq(typeof S.zero, 'function'); + eq(S.zero.length, 1); + eq(S.zero.toString(), 'zero :: Plus f => TypeRep f -> f a'); + + eq(S.zero(Array), []); + eq(S.zero(Object), {}); + eq(S.zero(S.Maybe), S.Nothing); + +});