sanctuary-type-classes
The Fantasy Land Specification "specifies interoperability of common
algebraic structures" by defining a number of type classes. For each type
class, it states laws which every member of a type must obey in order for
the type to be a member of the type class. In order for the Maybe type to
be considered a Functor, for example, every Maybe a
value must have
a fantasy-land/map
method which obeys the identity and composition laws.
This project provides:
TypeClass
, a function for defining type classes;- one
TypeClass
value for each Fantasy Land type class; - lawful Fantasy Land methods for JavaScript's built-in types;
- one function for each Fantasy Land method; and
- several functions derived from these functions.
Type-class hierarchy
Setoid Semigroup Foldable Functor Contravariant
(equals) (concat) (reduce) (map) (contramap)
| \ / | | | | \
| \ / | | | | \
| \ / | | | | \
| \ / | | | | \
| \ / | | | | \
Monoid Traversable | | | | \
(empty) (traverse) / | | \ \
/ | | \ \
/ / \ \ \
Profunctor / \ Bifunctor \
(promap) / \ (bimap) \
/ \ \
/ \ \
Alt Apply Extend
(alt) (ap) (extend)
/ / \ \
/ / \ \
/ / \ \
/ / \ \
/ / \ \
Plus Applicative Chain Comonad
(zero) (of) (chain) (extract)
\ / \ / \
\ / \ / \
\ / \ / \
\ / \ / \
\ / \ / \
Alternative Monad ChainRec
(chainRec)
API
The arguments are:
- the name of the type class, prefixed by its npm package name;
- an array of dependencies; and
- a predicate which accepts any JavaScript value and returns
true
if the value satisfies the requirements of the type class; false
otherwise.
Example:
const hasMethod = name => x => x != null && typeof x[name] == 'function';
const Foo = Z.TypeClass('my-package/Foo', [], hasMethod('foo'));
const Bar = Z.TypeClass('my-package/Bar', [Foo], hasMethod('bar'));
Types whose values have a foo
method are members of the Foo type class.
Members of the Foo type class whose values have a bar
method are also
members of the Bar type class.
Each TypeClass
value has a test
field: a function which accepts
any JavaScript value and returns true
if the value satisfies the
type class's predicate and the predicates of all the type class's
dependencies; false
otherwise.
TypeClass
values may be used with sanctuary-def
to define parametrically polymorphic functions which verify their
type-class constraints at run time.
TypeClass
value for Setoid.
> Setoid.test(null)
true
TypeClass
value for Semigroup.
> Semigroup.test('')
true
> Semigroup.test(0)
false
TypeClass
value for Monoid.
> Monoid.test('')
true
> Monoid.test(0)
false
TypeClass
value for Functor.
> Functor.test([])
true
> Functor.test('')
false
TypeClass
value for Bifunctor.
> Bifunctor.test(Tuple('foo', 64))
true
> Bifunctor.test([])
false
TypeClass
value for Profunctor.
> Profunctor.test(Math.sqrt)
true
> Profunctor.test([])
false
TypeClass
value for Apply.
> Apply.test([])
true
> Apply.test('')
false
TypeClass
value for Applicative.
> Applicative.test([])
true
> Applicative.test({})
false
TypeClass
value for Chain.
> Chain.test([])
true
> Chain.test({})
false
TypeClass
value for ChainRec.
> ChainRec.test([])
true
> ChainRec.test({})
false
TypeClass
value for Monad.
> Monad.test([])
true
> Monad.test({})
false
TypeClass
value for Alt.
> Alt.test({})
true
> Alt.test('')
false
TypeClass
value for Plus.
> Plus.test({})
true
> Plus.test('')
false
TypeClass
value for Alternative.
> Alternative.test([])
true
> Alternative.test({})
false
TypeClass
value for Foldable.
> Foldable.test({})
true
> Foldable.test('')
false
TypeClass
value for Traversable.
> Traversable.test([])
true
> Traversable.test({})
false
TypeClass
value for Extend.
> Extend.test([])
true
> Extend.test({})
false
TypeClass
value for Comonad.
> Comonad.test(Identity(0))
true
> Comonad.test([])
false
TypeClass
value for Contravariant.
> Contravariant.test(Math.sqrt)
true
> Contravariant.test([])
false
Returns a useful string representation of its argument.
Dispatches to the argument's toString
method if appropriate.
Where practical, equals(eval(toString(x)), x) = true
.
toString
implementations are provided for the following built-in types:
Null, Undefined, Boolean, Number, Date, String, Array, Arguments, Error,
and Object.
> toString(-0)
'-0'
> toString(['foo', 'bar', 'baz'])
'["foo", "bar", "baz"]'
> toString({x: 1, y: 2, z: 3})
'{"x": 1, "y": 2, "z": 3}'
> toString(Cons(1, Cons(2, Cons(3, Nil))))
'Cons(1, Cons(2, Cons(3, Nil)))'
Returns true
if its arguments are of the same type and equal according
to the type's fantasy-land/equals
method; false
otherwise.
fantasy-land/equals
implementations are provided for the following
built-in types: Null, Undefined, Boolean, Number, Date, RegExp, String,
Array, Arguments, Error, Object, and Function.
The algorithm supports circular data structures. Two arrays are equal
if they have the same index paths and for each path have equal values.
Two arrays which represent [1, [1, [1, [1, [1, ...]]]]]
, for example,
are equal even if their internal structures differ. Two objects are equal
if they have the same property paths and for each path have equal values.
> equals(0, -0)
false
> equals(NaN, NaN)
true
> equals(Cons('foo', Cons('bar', Nil)), Cons('foo', Cons('bar', Nil)))
true
> equals(Cons('foo', Cons('bar', Nil)), Cons('bar', Cons('foo', Nil)))
false
Function wrapper for fantasy-land/concat
.
fantasy-land/concat
implementations are provided for the following
built-in types: String, Array, and Object.
> concat('abc', 'def')
'abcdef'
> concat([1, 2, 3], [4, 5, 6])
[1, 2, 3, 4, 5, 6]
> concat({x: 1, y: 2}, {y: 3, z: 4})
{x: 1, y: 3, z: 4}
> concat(Cons('foo', Cons('bar', Cons('baz', Nil))), Cons('quux', Nil))
Cons('foo', Cons('bar', Cons('baz', Cons('quux', Nil))))
Function wrapper for fantasy-land/empty
.
fantasy-land/empty
implementations are provided for the following
built-in types: String, Array, and Object.
> empty(String)
''
> empty(Array)
[]
> empty(Object)
{}
> empty(List)
Nil
Function wrapper for fantasy-land/map
.
fantasy-land/map
implementations are provided for the following
built-in types: Array, Object, and Function.
> map(Math.sqrt, [1, 4, 9])
[1, 2, 3]
> map(Math.sqrt, {x: 1, y: 4, z: 9})
{x: 1, y: 2, z: 3}
> map(Math.sqrt, s => s.length)('Sanctuary')
3
> map(Math.sqrt, Tuple('foo', 64))
Tuple('foo', 8)
> map(Math.sqrt, Nil)
Nil
> map(Math.sqrt, Cons(1, Cons(4, Cons(9, Nil))))
Cons(1, Cons(2, Cons(3, Nil)))
Function wrapper for fantasy-land/bimap
.
> bimap(s => s.toUpperCase(), Math.sqrt, Tuple('foo', 64))
Tuple('FOO', 8)
Function wrapper for fantasy-land/promap
.
fantasy-land/promap
implementations are provided for the following
built-in types: Function.
> promap(Math.abs, x => x + 1, Math.sqrt)(-100)
11
Function wrapper for fantasy-land/ap
.
fantasy-land/ap
implementations are provided for the following
built-in types: Array, Object, and Function.
> ap([Math.sqrt, x => x * x], [1, 4, 9, 16, 25])
[1, 2, 3, 4, 5, 1, 16, 81, 256, 625]
> ap({a: Math.sqrt, b: x => x * x}, {a: 16, b: 10, c: 1})
{a: 4, b: 100}
> ap(s => n => s.slice(0, n), s => Math.ceil(s.length / 2))('Haskell')
'Hask'
> ap(Identity(Math.sqrt), Identity(64))
Identity(8)
> ap(Cons(Math.sqrt, Cons(x => x * x, Nil)), Cons(16, Cons(100, Nil)))
Cons(4, Cons(10, Cons(256, Cons(10000, Nil))))
Lifts a -> b -> c
to Apply f => f a -> f b -> f c
and returns the
result of applying this to the given arguments.
This function is derived from map
and ap
.
See also lift3
.
> lift2(x => y => Math.pow(x, y), [10], [1, 2, 3])
[10, 100, 1000]
> lift2(x => y => Math.pow(x, y), Identity(10), Identity(3))
Identity(1000)
Lifts a -> b -> c -> d
to Apply f => f a -> f b -> f c -> f d
and
returns the result of applying this to the given arguments.
This function is derived from map
and ap
.
See also lift2
.
> lift3(x => y => z => x + z + y, ['<'], ['>'], ['foo', 'bar', 'baz'])
['<foo>', '<bar>', '<baz>']
> lift3(x => y => z => x + z + y, Identity('<'), Identity('>'), Identity('baz'))
Identity('<baz>')
Combines two effectful actions, keeping only the result of the first.
Equivalent to Haskell's (<*)
function.
This function is derived from lift2
.
See also apSecond
.
> apFirst([1, 2], [3, 4])
[1, 1, 2, 2]
> apFirst(Identity(1), Identity(2))
Identity(1)
Combines two effectful actions, keeping only the result of the second.
Equivalent to Haskell's (*>)
function.
This function is derived from lift2
.
See also apFirst
.
> apSecond([1, 2], [3, 4])
[3, 4, 3, 4]
> apSecond(Identity(1), Identity(2))
Identity(2)
Function wrapper for fantasy-land/of
.
fantasy-land/of
implementations are provided for the following
built-in types: Array and Function.
> of(Array, 42)
[42]
> of(Function, 42)(null)
42
> of(List, 42)
Cons(42, Nil)
Function wrapper for fantasy-land/chain
.
fantasy-land/chain
implementations are provided for the following
built-in types: Array and Function.
> chain(x => [x, x], [1, 2, 3])
[1, 1, 2, 2, 3, 3]
> chain(x => x % 2 == 1 ? of(List, x) : Nil, Cons(1, Cons(2, Cons(3, Nil))))
Cons(1, Cons(3, Nil))
> chain(n => s => s.slice(0, n), s => Math.ceil(s.length / 2))('Haskell')
'Hask'
Removes one level of nesting from a nested monadic structure.
This function is derived from chain
.
> join([[1], [2], [3]])
[1, 2, 3]
> join([[[1, 2, 3]]])
[[1, 2, 3]]
> join(Identity(Identity(1)))
Identity(1)
Function wrapper for fantasy-land/chainRec
.
fantasy-land/chainRec
implementations are provided for the following
built-in types: Array.
> chainRec(
. Array,
. (next, done, s) => s.length == 2 ? [s + '!', s + '?'].map(done)
. : [s + 'o', s + 'n'].map(next),
. ''
. )
['oo!', 'oo?', 'on!', 'on?', 'no!', 'no?', 'nn!', 'nn?']
Filters its second argument in accordance with the given predicate.
This function is derived from concat
, empty
,
of
, and reduce
.
See also filterM
.
> filter(x => x % 2 == 1, [1, 2, 3])
[1, 3]
> filter(x => x % 2 == 1, Cons(1, Cons(2, Cons(3, Nil))))
Cons(1, Cons(3, Nil))
Filters its second argument in accordance with the given predicate.
This function is derived from of
, chain
, and
zero
.
See also filter
.
> filterM(x => x % 2 == 1, [1, 2, 3])
[1, 3]
> filterM(x => x % 2 == 1, Cons(1, Cons(2, Cons(3, Nil))))
Cons(1, Cons(3, Nil))
> filterM(x => x % 2 == 1, Nothing)
Nothing
> filterM(x => x % 2 == 1, Just(0))
Nothing
> filterM(x => x % 2 == 1, Just(1))
Just(1)
Function wrapper for fantasy-land/alt
.
fantasy-land/alt
implementations are provided for the following
built-in types: Array and Object.
> alt([1, 2, 3], [4, 5, 6])
[1, 2, 3, 4, 5, 6]
> alt(Nothing, Nothing)
Nothing
> alt(Nothing, Just(1))
Just(1)
> alt(Just(2), Just(3))
Just(2)
Function wrapper for fantasy-land/zero
.
fantasy-land/zero
implementations are provided for the following
built-in types: Array and Object.
> zero(Array)
[]
> zero(Object)
{}
> zero(Maybe)
Nothing
Function wrapper for fantasy-land/reduce
.
fantasy-land/reduce
implementations are provided for the following
built-in types: Array and Object.
> reduce((xs, x) => [x].concat(xs), [], [1, 2, 3])
[3, 2, 1]
> reduce(concat, '', Cons('foo', Cons('bar', Cons('baz', Nil))))
'foobarbaz'
Function wrapper for fantasy-land/traverse
.
fantasy-land/traverse
implementations are provided for the following
built-in types: Array.
See also sequence
.
> traverse(Array, x => x, [[1, 2, 3], [4, 5]])
[[1, 4], [1, 5], [2, 4], [2, 5], [3, 4], [3, 5]]
> traverse(Identity, x => Identity(x + 1), [1, 2, 3])
Identity([2, 3, 4])
Inverts the given t (f a)
to produce an f (t a)
.
This function is derived from traverse
.
> sequence(Array, Identity([1, 2, 3]))
[Identity(1), Identity(2), Identity(3)]
> sequence(Identity, [Identity(1), Identity(2), Identity(3)])
Identity([1, 2, 3])
Function wrapper for fantasy-land/extend
.
fantasy-land/extend
implementations are provided for the following
built-in types: Array.
> extend(xs => xs.length, ['foo', 'bar', 'baz', 'quux'])
[4]
Function wrapper for fantasy-land/extract
.
> extract(Identity(42))
42
Function wrapper for fantasy-land/contramap
.
fantasy-land/contramap
implementations are provided for the following
built-in types: Function.
> contramap(s => s.length, Math.sqrt)('Sanctuary')
3