Specification for interoperability of common algebraic structures in JavaScript
npm install fantasy-land 
(aka "Algebraic JavaScript Specification")

This project specifies interoperability of common algebraic
structures:
* Setoid
* Ord
* Semigroupoid
* Category
* Semigroup
* Monoid
* Group
* Filterable
* Functor
* Contravariant
* Apply
* Applicative
* Alt
* Plus
* Alternative
* Foldable
* Traversable
* Chain
* ChainRec
* Monad
* Extend
* Comonad
* Bifunctor
* Profunctor

An algebra is a set of values, a set of operators that it is closed
under and some laws it must obey.
Each Fantasy Land algebra is a separate specification. An algebra may
have dependencies on other algebras which must be implemented.
1. "value" is any JavaScript value, including any which have the
structures defined below.
2. "equivalent" is an appropriate definition of equivalence for the given value.
The definition should ensure that the two values can be safely swapped out in a program that respects abstractions. For example:
- Two lists are equivalent if they are equivalent at all indices.
- Two plain old JavaScript objects, interpreted as dictionaries, are equivalent when they are equivalent for all keys.
- Two promises are equivalent when they yield equivalent values.
- Two functions are equivalent if they yield equivalent outputs for equivalent inputs.
The type signature notation used in this document is described below:id="sanctuary-types-return">1
* :: _"is a member of"._
- e :: t can be read as: "the expression e is a member of type t".
- true :: Boolean - "true is a member of type Boolean".
- 42 :: Integer, Number - "42 is a member of the Integer and
Number types".
* _New types can be created via type constructors._
- Type constructors can take zero or more type arguments.
- Array is a type constructor which takes one type argument.
- Array String is the type of all arrays of strings. Each of the
following has type Array String: [], ['foo', 'bar', 'baz'].
- Array (Array String) is the type of all arrays of arrays of strings.
Each of the following has type Array (Array String): [], [ [], [],
][ [], ['foo'], ['bar', 'baz'] ].
* _Lowercase letters stand for type variables._
- Type variables can take any type unless they have been restricted by
means of type constraints (see fat arrow below).
* -> (arrow) _Function type constructor._
- -> is an _infix_ type constructor that takes two type arguments where
left argument is the input type and the right argument is the output type.
- ->'s input type can be a grouping of types to create the type of a
function which accepts zero or more arguments. The syntax is:
(, where comprises zero
or more comma–space (, )-separated type representations and parens
may be omitted for unary functions.
- String -> Array String is a type satisfied by functions which take a
String and return an Array String.
- String -> Array String -> Array String is a type satisfied by functions
which take a String and return a function which takes an Array String
and returns an Array String.
- (String, Array String) -> Array String is a type satisfied by functions
which take a String and an Array String as arguments and return an
Array String.
- () -> Number is a type satisfied by functions
which do not take arguments and return a Number.
* ~> (squiggly arrow) _Method type constructor._
- When a function is a property of an Object, it is called a method. All
methods have an implicit parameter type - the type of which they are a
property.
- a ~> a -> a is a type satisfied by methods on Objects of type a which
take a type a as an argument and return a value of type a.
* => (fat arrow) _Expresses constraints on type variables._
- In a ~> a -> a (see squiggly arrow above), a can be of any type.
Semigroup a => a ~> a -> a adds a constraint such that the type a
must now satisfy the Semigroup typeclass. To satisfy a typeclass means
to lawfully implement all functions/methods specified by that typeclass.
For example:
```
fantasy-land/traverse :: Applicative f, Traversable t => t a ~> (TypeRep f, a -> f b) -> f (t b)
'-------------------' '--------------------------' '-' '-------------------' '-----'
' ' ' ' '
' ' - type constraints ' ' - argument types ' - return type
' '
'- method name ' - method target type
- - -
1. See the Types
section in Sanctuary's docs for more info. ↩
Certain behaviours are defined from the perspective of a member of a type.
Other behaviours do not require a member. Thus certain algebras require a
type to provide a value-level representative (with certain properties). The
Identity type, for example, could provide Id as its type representative:Id :: TypeRep Identity.
If a type provides a type representative, each member of the type must have
a constructor property which is a reference to the type representative.
1. a'fantasy-land/equals' === true (reflexivity)a'fantasy-land/equals' === b'fantasy-land/equals'
2. (symmetry)a'fantasy-land/equals'
3. If and b'fantasy-land/equals', then a'fantasy-land/equals' (transitivity)
#### fantasy-land/equals method
`hs`
fantasy-land/equals :: Setoid a => a ~> a -> Boolean
A value which has a Setoid must provide a fantasy-land/equals method. Thefantasy-land/equals method takes one argument:
1. b must be a value of the same Setoid
1. If b is not the same Setoid, behaviour of fantasy-land/equals isfalse
unspecified (returning is recommended).
2. fantasy-land/equals must return a boolean (true or false).
A value that implements the Ord specification must also implement
the Setoid specification.
1. a'fantasy-land/lte' or b'fantasy-land/lte' (totality)a'fantasy-land/lte'
2. If and b'fantasy-land/lte', then a'fantasy-land/equals' (antisymmetry)a'fantasy-land/lte'
3. If and b'fantasy-land/lte', then a'fantasy-land/lte' (transitivity)
#### fantasy-land/lte method
`hs`
fantasy-land/lte :: Ord a => a ~> a -> Boolean
A value which has an Ord must provide a fantasy-land/lte method. Thefantasy-land/lte method takes one argument:
1. b must be a value of the same Ord
1. If b is not the same Ord, behaviour of fantasy-land/lte isfalse
unspecified (returning is recommended).
2. fantasy-land/lte must return a boolean (true or false).
1. a'fantasy-land/compose''fantasy-land/compose' === a'fantasy-land/compose') (associativity)
#### fantasy-land/compose method
`hs`
fantasy-land/compose :: Semigroupoid c => c i j ~> c j k -> c i k
A value which has a Semigroupoid must provide a fantasy-land/compose method. Thefantasy-land/compose method takes one argument:
1. b must be a value of the same Semigroupoid
1. If b is not the same semigroupoid, behaviour of fantasy-land/compose is
unspecified.
2. fantasy-land/compose must return a value of the same Semigroupoid.
A value that implements the Category specification must also implement
the Semigroupoid specification.
1. a'fantasy-land/compose') is equivalent to a (right identity)C['fantasy-land/id']()'fantasy-land/compose'
2. is equivalent to a (left identity)
#### fantasy-land/id method
`hs`
fantasy-land/id :: Category c => () -> c a a
A value which has a Category must provide a fantasy-land/id function on its
type representative:
C['fantasy-land/id']()
Given a value c, one can access its type representative via theconstructor property:
c.constructor['fantasy-land/id']()
1. fantasy-land/id must return a value of the same Category
1. a'fantasy-land/concat''fantasy-land/concat' is equivalent to a'fantasy-land/concat') (associativity)
#### fantasy-land/concat method
`hs`
fantasy-land/concat :: Semigroup a => a ~> a -> a
A value which has a Semigroup must provide a fantasy-land/concat method. Thefantasy-land/concat method takes one argument:
1. b must be a value of the same Semigroup
1. If b is not the same semigroup, behaviour of fantasy-land/concat is
unspecified.
2. fantasy-land/concat must return a value of the same Semigroup.
A value that implements the Monoid specification must also implement
the Semigroup specification.
1. m'fantasy-land/concat') is equivalent to m (right identity)M['fantasy-land/empty']()'fantasy-land/concat'
2. is equivalent to m (left identity)
#### fantasy-land/empty method
`hs`
fantasy-land/empty :: Monoid m => () -> m
A value which has a Monoid must provide a fantasy-land/empty function on its
type representative:
M['fantasy-land/empty']()
Given a value m, one can access its type representative via theconstructor property:
m.constructor['fantasy-land/empty']()
1. fantasy-land/empty must return a value of the same Monoid
A value that implements the Group specification must also implement
the Monoid specification.
1. g'fantasy-land/concat') is equivalent to g.constructor['fantasy-land/empty']() (right inverse)g['fantasy-land/invert']()'fantasy-land/concat'
2. is equivalent to g.constructor['fantasy-land/empty']() (left inverse)
#### fantasy-land/invert method
`hs`
fantasy-land/invert :: Group g => g ~> () -> g
A value which has a Group must provide a fantasy-land/invert method. Thefantasy-land/invert method takes no arguments:
g['fantasy-land/invert']()
1. fantasy-land/invert must return a value of the same Group.
1. v'fantasy-land/filter' && q(x)) is equivalent to v'fantasy-land/filter''fantasy-land/filter' (distributivity)v'fantasy-land/filter'
2. is equivalent to v (identity)v'fantasy-land/filter'
3. is equivalent to w'fantasy-land/filter'v
if and w are values of the same Filterable (annihilation)
#### fantasy-land/filter method
`hs`
fantasy-land/filter :: Filterable f => f a ~> (a -> Boolean) -> f a
A value which has a Filterable must provide a fantasy-land/filter method. The fantasy-land/filter
method takes one argument:
1. p must be a function.
1. If p is not a function, the behaviour of fantasy-land/filter is unspecified.p
2. must return either true or false. If it returns any other value,fantasy-land/filter
the behaviour of is unspecified.
2. fantasy-land/filter must return a value of the same Filterable.
1. u'fantasy-land/map' is equivalent to u (identity)u'fantasy-land/map'))
2. is equivalent to u'fantasy-land/map''fantasy-land/map' (composition)
#### fantasy-land/map method
`hs`
fantasy-land/map :: Functor f => f a ~> (a -> b) -> f b
A value which has a Functor must provide a fantasy-land/map method. The fantasy-land/map
method takes one argument:
1. f must be a function,
1. If f is not a function, the behaviour of fantasy-land/map isf
unspecified.
2. can return any value.f
3. No parts of 's return value should be checked.
2. fantasy-land/map must return a value of the same Functor
1. u'fantasy-land/contramap' is equivalent to u (identity)u'fantasy-land/contramap'))
2. is equivalent to u'fantasy-land/contramap''fantasy-land/contramap'
(composition)
#### fantasy-land/contramap method
`hs`
fantasy-land/contramap :: Contravariant f => f a ~> (b -> a) -> f b
A value which has a Contravariant must provide a fantasy-land/contramap method. Thefantasy-land/contramap method takes one argument:
1. f must be a function,
1. If f is not a function, the behaviour of fantasy-land/contramap isf
unspecified.
2. can return any value.f
3. No parts of 's return value should be checked.
2. fantasy-land/contramap must return a value of the same Contravariant
A value that implements the Apply specification must also
implement the Functor specification.
1. v'fantasy-land/ap')))) is equivalent to v'fantasy-land/ap''fantasy-land/ap' (composition)
#### fantasy-land/ap method
`hs`
fantasy-land/ap :: Apply f => f a ~> f (a -> b) -> f b
A value which has an Apply must provide a fantasy-land/ap method. The fantasy-land/ap
method takes one argument:
1. b must be an Apply of a function
1. If b does not represent a function, the behaviour of fantasy-land/ap isb
unspecified.
2. must be same Apply as a.
2. a must be an Apply of any value
3. fantasy-land/ap must apply the function in Apply b to the value ina
Apply
1. No parts of return value of that function should be checked.
4. The Apply returned by fantasy-land/ap must be the same as a and b
A value that implements the Applicative specification must also
implement the Apply specification.
1. v'fantasy-land/ap') is equivalent to v (identity)A'fantasy-land/of''fantasy-land/ap')
2. is equivalent to A'fantasy-land/of') (homomorphism)A'fantasy-land/of''fantasy-land/ap'
3. is equivalent to u'fantasy-land/ap')) (interchange)
#### fantasy-land/of method
`hs`
fantasy-land/of :: Applicative f => a -> f a
A value which has an Applicative must provide a fantasy-land/of function on itsfantasy-land/of
type representative. The function takes
one argument:
Given a value f, one can access its type representative via theconstructor property:
f.constructor'fantasy-land/of'
1. fantasy-land/of must provide a value of the same Applicative
1. No parts of a should be checked
A value that implements the Alt specification must also implement
the Functor specification.
1. a'fantasy-land/alt''fantasy-land/alt' is equivalent to a'fantasy-land/alt') (associativity)a'fantasy-land/alt''fantasy-land/map'
2. is equivalent to a'fantasy-land/map''fantasy-land/alt') (distributivity)
#### fantasy-land/alt method
`hs`
fantasy-land/alt :: Alt f => f a ~> f a -> f a
A value which has a Alt must provide a fantasy-land/alt method. Thefantasy-land/alt method takes one argument:
1. b must be a value of the same Alt
1. If b is not the same Alt, behaviour of fantasy-land/alt isa
unspecified.
2. and b can contain any value of same type.a
3. No parts of 's and b's containing value should be checked.
2. fantasy-land/alt must return a value of the same Alt.
A value that implements the Plus specification must also implement
the Alt specification.
1. x'fantasy-land/alt') is equivalent to x (right identity)A['fantasy-land/zero']()'fantasy-land/alt'
2. is equivalent to x (left identity)A['fantasy-land/zero']()'fantasy-land/map'
3. is equivalent to A['fantasy-land/zero']() (annihilation)
#### fantasy-land/zero method
`hs`
fantasy-land/zero :: Plus f => () -> f a
A value which has a Plus must provide a fantasy-land/zero function on its
type representative:
A['fantasy-land/zero']()
Given a value x, one can access its type representative via theconstructor property:
x.constructor['fantasy-land/zero']()
1. fantasy-land/zero must return a value of the same Plus
A value that implements the Alternative specification must also implement
the Applicative and Plus specifications.
1. x'fantasy-land/ap') is equivalent to x'fantasy-land/ap''fantasy-land/alt') (distributivity)x'fantasy-land/ap')
2. is equivalent to A['fantasy-land/zero']() (annihilation)
1. u['fantasy-land/reduce'] is equivalent to u'fantasy-land/reduce' => acc.concat([x]), []).reduce
#### fantasy-land/reduce method
`hs`
fantasy-land/reduce :: Foldable f => f a ~> ((b, a) -> b, b) -> b
A value which has a Foldable must provide a fantasy-land/reduce method. The fantasy-land/reduce
method takes two arguments:
1. f must be a binary function
1. if f is not a function, the behaviour of fantasy-land/reduce is unspecified.f
2. The first argument to must be the same type as x.f
3. must return a value of the same type as x.f
4. No parts of 's return value should be checked.
1. x is the initial accumulator value for the reduction
1. No parts of x should be checked.
A value that implements the Traversable specification must also
implement the Functor and Foldable specifications.
1. t(u'fantasy-land/traverse') is equivalent to u'fantasy-land/traverse' for anyt
such that t(a)'fantasy-land/map' is equivalent to t(a'fantasy-land/map') (naturality)
2. u'fantasy-land/traverse' is equivalent to F'fantasy-land/of' for any Applicative F
(identity)
3. u'fantasy-land/traverse') is equivalent tonew Compose(u'fantasy-land/traverse''fantasy-land/map'))
forCompose
defined below and any Applicatives F and G (composition)
`js
function Compose(c) {
this.c = c;
}
Compose['fantasy-land/of'] = function(x) {
return new Compose(F'fantasy-land/of'));
};
Compose.prototype['fantasy-land/ap'] = function(f) {
return new Compose(this.c'fantasy-land/ap')));
};
Compose.prototype['fantasy-land/map'] = function(f) {
return new Compose(this.c'fantasy-land/map'));
};
`
#### fantasy-land/traverse method
`hs`
fantasy-land/traverse :: Applicative f, Traversable t => t a ~> (TypeRep f, a -> f b) -> f (t b)
A value which has a Traversable must provide a fantasy-land/traverse method. The fantasy-land/traverse
method takes two arguments:
1. A must be the type representative of an
Applicative.
2. f must be a function which returns a value
1. If f is not a function, the behaviour of fantasy-land/traverse isf
unspecified.
2. must return a value of the type represented by A.
3. fantasy-land/traverse must return a value of the type represented by A.
A value that implements the Chain specification must also
implement the Apply specification.
1. m'fantasy-land/chain''fantasy-land/chain' is equivalent to m'fantasy-land/chain''fantasy-land/chain') (associativity)
#### fantasy-land/chain method
`hs`
fantasy-land/chain :: Chain m => m a ~> (a -> m b) -> m b
A value which has a Chain must provide a fantasy-land/chain method. The fantasy-land/chain
method takes one argument:
1. f must be a function which returns a value
1. If f is not a function, the behaviour of fantasy-land/chain isf
unspecified.
2. must return a value of the same Chain
2. fantasy-land/chain must return a value of the same Chain
A value that implements the ChainRec specification must also implement the Chain specification.
1. M'fantasy-land/chainRec' => p(v) ? d(v)'fantasy-land/map' : n(v)'fantasy-land/map', i)(function step(v) { return p(v) ? d(v) : n(v)'fantasy-land/chain'; }(i))
is equivalent to
(equivalence)M'fantasy-land/chainRec'
2. Stack usage of must be at most a constant multiple of the stack usage of f itself.
#### fantasy-land/chainRec method
`hs`
fantasy-land/chainRec :: ChainRec m => ((a -> c, b -> c, a) -> m c, a) -> m b
A Type which has a ChainRec must provide a fantasy-land/chainRec function on itsfantasy-land/chainRec
type representative. The function
takes two arguments:
Given a value m, one can access its type representative via theconstructor property:
m.constructor'fantasy-land/chainRec'
1. f must be a function which returns a valuef
1. If is not a function, the behaviour of fantasy-land/chainRec is unspecified.f
2. takes three arguments next, done, valuenext
1. is a function which takes one argument of same type as i and can return any valuedone
2. is a function which takes one argument and returns the same type as the return value of nextvalue
3. is some value of the same type as if
3. must return a value of the same ChainRec which contains a value returned from either done or nextfantasy-land/chainRec
2. must return a value of the same ChainRec which contains a value of same type as argument of done
A value that implements the Monad specification must also implement
the Applicative and Chain specifications.
1. M'fantasy-land/of''fantasy-land/chain' is equivalent to f(a) (left identity)m'fantasy-land/chain'
2. is equivalent to m (right identity)
A value that implements the Extend specification must also implement the Functor specification.
1. w'fantasy-land/extend''fantasy-land/extend' is equivalent to w'fantasy-land/extend'))
#### fantasy-land/extend method
`hs`
fantasy-land/extend :: Extend w => w a ~> (w a -> b) -> w b
An Extend must provide a fantasy-land/extend method. The fantasy-land/extend
method takes one argument:
1. f must be a function which returns a value
1. If f is not a function, the behaviour of fantasy-land/extend isf
unspecified.
2. must return a value of type v, for some variable v contained in w.f
3. No parts of 's return value should be checked.
2. fantasy-land/extend must return a value of the same Extend.
A value that implements the Comonad specification must also implement the Extend specification.
1. w'fantasy-land/extend') is equivalent to w (left identity)w'fantasy-land/extend'['fantasy-land/extract']()
2. is equivalent to f(w) (right identity)
#### fantasy-land/extract method
`hs`
fantasy-land/extract :: Comonad w => w a ~> () -> a
A value which has a Comonad must provide a fantasy-land/extract method on itself.fantasy-land/extract
The method takes no arguments:
w['fantasy-land/extract']()
1. fantasy-land/extract must return a value of type v, for some variable v contained in w.v
1. must have the same type that f returns in fantasy-land/extend.
A value that implements the Bifunctor specification must also implement
the Functor specification.
1. p'fantasy-land/bimap' is equivalent to p (identity)p'fantasy-land/bimap'), b => h(i(b))
2. is equivalent to p'fantasy-land/bimap''fantasy-land/bimap' (composition)
#### fantasy-land/bimap method
`hs`
fantasy-land/bimap :: Bifunctor f => f a c ~> (a -> b, c -> d) -> f b d
A value which has a Bifunctor must provide a fantasy-land/bimap method. The fantasy-land/bimap
method takes two arguments:
1. f must be a function which returns a value
1. If f is not a function, the behaviour of fantasy-land/bimap is unspecified.f
2. can return any value.f
3. No parts of 's return value should be checked.
2. g must be a function which returns a value
1. If g is not a function, the behaviour of fantasy-land/bimap is unspecified.g
2. can return any value.g
3. No parts of 's return value should be checked.
3. fantasy-land/bimap must return a value of the same Bifunctor.
A value that implements the Profunctor specification must also implement
the Functor specification.
1. p'fantasy-land/promap' is equivalent to p (identity)p'fantasy-land/promap'), b => h(i(b)))
2. is equivalent to p'fantasy-land/promap''fantasy-land/promap' (composition)
#### fantasy-land/promap method
`hs`
fantasy-land/promap :: Profunctor p => p b c ~> (a -> b, c -> d) -> p a d
A value which has a Profunctor must provide a fantasy-land/promap method.
The fantasy-land/promap method takes two arguments:
1. f must be a function which returns a value
1. If f is not a function, the behaviour of fantasy-land/promap is unspecified.f
2. can return any value.f
3. No parts of 's return value should be checked.
2. g must be a function which returns a value
1. If g is not a function, the behaviour of fantasy-land/promap is unspecified.g
2. can return any value.g
3. No parts of 's return value should be checked.
3. fantasy-land/promap must return a value of the same Profunctor
When creating data types which satisfy multiple algebras, authors may choose
to implement certain methods then derive the remaining methods. Derivations:
- [fantasy-land/equals][] may be derived from [fantasy-land/lte][]:
`js`
function equals(other) { return this'fantasy-land/lte' && other'fantasy-land/lte'; }
- [fantasy-land/map][] may be derived from [fantasy-land/ap][] and [fantasy-land/of][]:
`js`
function map(f) { return this'fantasy-land/ap'); }
- [fantasy-land/map][] may be derived from [fantasy-land/chain][] and [fantasy-land/of][]:
`js`
function map(f) { return this'fantasy-land/chain')); }
- [fantasy-land/map][] may be derived from [fantasy-land/bimap][]:
`js`
function map(f) { return this'fantasy-land/bimap'; }
- [fantasy-land/map][] may be derived from [fantasy-land/promap][]:
`js`
function map(f) { return this'fantasy-land/promap'; }
- [fantasy-land/ap][] may be derived from [fantasy-land/chain][]:
`js`
function ap(m) { return m'fantasy-land/chain'); }
- [fantasy-land/reduce][] may be derived as follows:
`js`
function reduce(f, acc) {
function Const(value) {
this.value = value;
}
Const['fantasy-land/of'] = function(_) {
return new Const(acc);
};
Const.prototype['fantasy-land/map'] = function(_) {
return this;
};
Const.prototype['fantasy-land/ap'] = function(b) {
return new Const(f(b.value, this.value));
};
return this'fantasy-land/traverse', Const['fantasy-land/of']).value;
}
- [fantasy-land/map][] may be derived as follows:
`js`
function map(f) {
function Id(value) {
this.value = value;
}
Id['fantasy-land/of'] = function(x) {
return new Id(x);
};
Id.prototype['fantasy-land/map'] = function(f) {
return new Id(f(this.value));
};
Id.prototype['fantasy-land/ap'] = function(b) {
return new Id(this.value(b.value));
};
return this'fantasy-land/traverse'), Id['fantasy-land/of']).value;
}
- [fantasy-land/filter][] may be derived from [fantasy-land/of][], [fantasy-land/chain][], and [fantasy-land/zero][]:
`js`
function filter(pred) {
var F = this.constructor;
return this'fantasy-land/chain' ? F'fantasy-land/of' : F['fantasy-land/zero']());
}
- [fantasy-land/filter][] may be derived from [fantasy-land/concat][], [fantasy-land/of][], [fantasy-land/zero][], andfantasy-land/reduce
[][]:
`js`
function filter(pred) {
var F = this.constructor;
return this'fantasy-land/reduce' => pred(x) ? f'fantasy-land/concat') : f, F['fantasy-land/zero']());
}
If a data type provides a method which could be derived, its behaviour must
be equivalent to that of the derivation (or derivations).
1. If there's more than a single way to implement the methods and
laws, the implementation should choose one and provide wrappers for
other uses.
2. It's discouraged to overload the specified methods. It can easily
result in broken and buggy behaviour.
3. It is recommended to throw an exception on unspecified behaviour.
4. An Identity container which implements many of the methods is provided by
sanctuary-identity.
[fantasy-land/ap]: #ap-methodfantasy-land/bimap
[]: #bimap-methodfantasy-land/chain
[]: #chain-methodfantasy-land/concat
[]: #concat-methodfantasy-land/equals
[]: #equals-methodfantasy-land/filter
[]: #filter-methodfantasy-land/lte
[]: #lte-methodfantasy-land/map
[]: #map-methodfantasy-land/of
[]: #of-methodfantasy-land/promap
[]: #promap-methodfantasy-land/reduce
[]: #reduce-methodfantasy-land/zero`]: #zero-method
[
There also exists Static Land Specification
with exactly the same ideas as Fantasy Land but based on static methods instead of instance methods.