fantasyland/fantasy-land
{ "createdAt": "2013-04-12T03:10:22Z", "defaultBranch": "master", "description": "Specification for interoperability of common algebraic structures in JavaScript", "fullName": "fantasyland/fantasy-land", "homepage": null, "language": "JavaScript", "name": "fantasy-land", "pushedAt": "2024-11-10T21:50:53Z", "stargazersCount": 10224, "topics": [ "fantasy-land", "functional", "javascript", "specification" ], "updatedAt": "2025-11-23T19:26:01Z", "url": "https://github.com/fantasyland/fantasy-land"}Fantasy Land Specification
Section titled “Fantasy Land Specification”(aka “Algebraic JavaScript Specification”)
This project specifies interoperability of common algebraic structures:
Setoid Semigroupoid Semigroup Foldable Functor Contravariant Filterable (equals) (compose) (concat) (reduce) (map) (contramap) (filter) | | | \ / | | | | \ | | | \ / | | | | \ | | | \ / | | | | \ | | | \ / | | | | \ | | | \ / | | | | \ Ord Category Monoid Traversable | | | | \ (lte) (id) (empty) (traverse) / | | \ \ | / | | \ \ | / / \ \ \ | Profunctor / \ Bifunctor \ | (promap) / \ (bimap) \ | / \ \ Group / \ \ (invert) Alt Apply Extend (alt) (ap) (extend) / / \ \ / / \ \ / / \ \ / / \ \ / / \ \ Plus Applicative Chain Comonad (zero) (of) (chain) (extract) \ / \ / \ \ / \ / \ \ / \ / \ \ / \ / \ \ / \ / \ Alternative Monad ChainRec (chainRec)
General
Section titled “General”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.
Terminology
Section titled “Terminology”- “value” is any JavaScript value, including any which have the structures defined below.
- “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.
Type signature notation
Section titled “Type signature notation”The type signature notation used in this document is described below:[1]!(#sanctuary-types)
::“is a member of”.e :: tcan be read as: “the expressioneis a member of typet”.true :: Boolean- “trueis a member of typeBoolean”.42 :: Integer, Number- “42is a member of theIntegerandNumbertypes”.
- New types can be created via type constructors.
- Type constructors can take zero or more type arguments.
Arrayis a type constructor which takes one type argument.Array Stringis the type of all arrays of strings. Each of the following has typeArray String:[],['foo', 'bar', 'baz'].Array (Array String)is the type of all arrays of arrays of strings. Each of the following has typeArray (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:(<input-types>) -> <output-type>, where<input-types>comprises zero or more comma–space (,)-separated type representations and parens may be omitted for unary functions.String -> Array Stringis a type satisfied by functions which take aStringand return anArray String.String -> Array String -> Array Stringis a type satisfied by functions which take aStringand return a function which takes anArray Stringand returns anArray String.(String, Array String) -> Array Stringis a type satisfied by functions which take aStringand anArray Stringas arguments and return anArray String.() -> Numberis a type satisfied by functions which do not take arguments and return aNumber.
~>(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 -> ais a type satisfied by methods on Objects of typeawhich take a typeaas an argument and return a value of typea.
=>(fat arrow) Expresses constraints on type variables.- In
a ~> a -> a(see squiggly arrow above),acan be of any type.Semigroup a => a ~> a -> aadds a constraint such that the typeamust now satisfy theSemigrouptypeclass. To satisfy a typeclass means to lawfully implement all functions/methods specified by that typeclass.
- In
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- See the Types section in Sanctuary’s docs for more info. [↩]!(#sanctuary-types-return)
Type representatives
Section titled “Type representatives”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.
Algebras
Section titled “Algebras”Setoid
Section titled “Setoid”a['fantasy-land/equals']!(a) === true(reflexivity)a['fantasy-land/equals']!(b) === b['fantasy-land/equals']!(a)(symmetry)- If
a['fantasy-land/equals']!(b)andb['fantasy-land/equals']!(c), thena['fantasy-land/equals']!(c)(transitivity)
fantasy-land/equals method
Section titled “fantasy-land/equals method”fantasy-land/equals :: Setoid a => a ~> a -> BooleanA value which has a Setoid must provide a fantasy-land/equals method. The
fantasy-land/equals method takes one argument:
a['fantasy-land/equals']!(b)-
bmust be a value of the same Setoid- If
bis not the same Setoid, behaviour offantasy-land/equalsis unspecified (returningfalseis recommended).
- If
-
fantasy-land/equalsmust return a boolean (trueorfalse).
A value that implements the Ord specification must also implement the [Setoid]!(#setoid) specification.
a['fantasy-land/lte']!(b)orb['fantasy-land/lte']!(a)(totality)- If
a['fantasy-land/lte']!(b)andb['fantasy-land/lte']!(a), thena['fantasy-land/equals']!(b)(antisymmetry) - If
a['fantasy-land/lte']!(b)andb['fantasy-land/lte']!(c), thena['fantasy-land/lte']!(c)(transitivity)
fantasy-land/lte method
Section titled “fantasy-land/lte method”fantasy-land/lte :: Ord a => a ~> a -> BooleanA value which has an Ord must provide a fantasy-land/lte method. The
fantasy-land/lte method takes one argument:
a['fantasy-land/lte']!(b)-
bmust be a value of the same Ord- If
bis not the same Ord, behaviour offantasy-land/lteis unspecified (returningfalseis recommended).
- If
-
fantasy-land/ltemust return a boolean (trueorfalse).
Semigroupoid
Section titled “Semigroupoid”a['fantasy-land/compose']!(b)['fantasy-land/compose']!(c) === a['fantasy-land/compose']!(b['fantasy-land/compose']!(c))(associativity)
fantasy-land/compose method
Section titled “fantasy-land/compose method”fantasy-land/compose :: Semigroupoid c => c i j ~> c j k -> c i kA value which has a Semigroupoid must provide a fantasy-land/compose method. The
fantasy-land/compose method takes one argument:
a['fantasy-land/compose']!(b)-
bmust be a value of the same Semigroupoid- If
bis not the same semigroupoid, behaviour offantasy-land/composeis unspecified.
- If
-
fantasy-land/composemust return a value of the same Semigroupoid.
Category
Section titled “Category”A value that implements the Category specification must also implement the [Semigroupoid]!(#semigroupoid) specification.
a['fantasy-land/compose']!(C['fantasy-land/id']!())is equivalent toa(right identity)C['fantasy-land/id']!()['fantasy-land/compose']!(a)is equivalent toa(left identity)
fantasy-land/id method
Section titled “fantasy-land/id method”fantasy-land/id :: Category c => () -> c a aA value which has a Category must provide a fantasy-land/id function on its
[type representative]!(#type-representatives):
C['fantasy-land/id']!()Given a value c, one can access its type representative via the
constructor property:
c.constructor['fantasy-land/id']!()fantasy-land/idmust return a value of the same Category
Semigroup
Section titled “Semigroup”a['fantasy-land/concat']!(b)['fantasy-land/concat']!(c)is equivalent toa['fantasy-land/concat']!(b['fantasy-land/concat']!(c))(associativity)
fantasy-land/concat method
Section titled “fantasy-land/concat method”fantasy-land/concat :: Semigroup a => a ~> a -> aA value which has a Semigroup must provide a fantasy-land/concat method. The
fantasy-land/concat method takes one argument:
s['fantasy-land/concat']!(b)-
bmust be a value of the same Semigroup- If
bis not the same semigroup, behaviour offantasy-land/concatis unspecified.
- If
-
fantasy-land/concatmust return a value of the same Semigroup.
Monoid
Section titled “Monoid”A value that implements the Monoid specification must also implement the [Semigroup]!(#semigroup) specification.
m['fantasy-land/concat']!(M['fantasy-land/empty']!())is equivalent tom(right identity)M['fantasy-land/empty']!()['fantasy-land/concat']!(m)is equivalent tom(left identity)
fantasy-land/empty method
Section titled “fantasy-land/empty method”fantasy-land/empty :: Monoid m => () -> mA value which has a Monoid must provide a fantasy-land/empty function on its
[type representative]!(#type-representatives):
M['fantasy-land/empty']!()Given a value m, one can access its type representative via the
constructor property:
m.constructor['fantasy-land/empty']!()fantasy-land/emptymust return a value of the same Monoid
A value that implements the Group specification must also implement the [Monoid]!(#monoid) specification.
g['fantasy-land/concat']!(g['fantasy-land/invert']!())is equivalent tog.constructor['fantasy-land/empty']!()(right inverse)g['fantasy-land/invert']!()['fantasy-land/concat']!(g)is equivalent tog.constructor['fantasy-land/empty']!()(left inverse)
fantasy-land/invert method
Section titled “fantasy-land/invert method”fantasy-land/invert :: Group g => g ~> () -> gA value which has a Group must provide a fantasy-land/invert method. The
fantasy-land/invert method takes no arguments:
g['fantasy-land/invert']!()fantasy-land/invertmust return a value of the same Group.
Filterable
Section titled “Filterable”v['fantasy-land/filter']!(x => p(x) && q(x))is equivalent tov['fantasy-land/filter']!(p)['fantasy-land/filter']!(q)(distributivity)v['fantasy-land/filter']!(x => true)is equivalent tov(identity)v['fantasy-land/filter']!(x => false)is equivalent tow['fantasy-land/filter']!(x => false)ifvandware values of the same Filterable (annihilation)
fantasy-land/filter method
Section titled “fantasy-land/filter method”fantasy-land/filter :: Filterable f => f a ~> (a -> Boolean) -> f aA value which has a Filterable must provide a fantasy-land/filter method. The fantasy-land/filter
method takes one argument:
v['fantasy-land/filter']!(p)-
pmust be a function.- If
pis not a function, the behaviour offantasy-land/filteris unspecified. pmust return eithertrueorfalse. If it returns any other value, the behaviour offantasy-land/filteris unspecified.
- If
-
fantasy-land/filtermust return a value of the same Filterable.
Functor
Section titled “Functor”u['fantasy-land/map']!(a => a)is equivalent tou(identity)u['fantasy-land/map']!(x => f(g(x)))is equivalent tou['fantasy-land/map']!(g)['fantasy-land/map']!(f)(composition)
fantasy-land/map method
Section titled “fantasy-land/map method”fantasy-land/map :: Functor f => f a ~> (a -> b) -> f bA value which has a Functor must provide a fantasy-land/map method. The fantasy-land/map
method takes one argument:
u['fantasy-land/map']!(f)-
fmust be a function,- If
fis not a function, the behaviour offantasy-land/mapis unspecified. fcan return any value.- No parts of
f’s return value should be checked.
- If
-
fantasy-land/mapmust return a value of the same Functor
Contravariant
Section titled “Contravariant”u['fantasy-land/contramap']!(a => a)is equivalent tou(identity)u['fantasy-land/contramap']!(x => f(g(x)))is equivalent tou['fantasy-land/contramap']!(f)['fantasy-land/contramap']!(g)(composition)
fantasy-land/contramap method
Section titled “fantasy-land/contramap method”fantasy-land/contramap :: Contravariant f => f a ~> (b -> a) -> f bA value which has a Contravariant must provide a fantasy-land/contramap method. The
fantasy-land/contramap method takes one argument:
u['fantasy-land/contramap']!(f)-
fmust be a function,- If
fis not a function, the behaviour offantasy-land/contramapis unspecified. fcan return any value.- No parts of
f’s return value should be checked.
- If
-
fantasy-land/contramapmust return a value of the same Contravariant
A value that implements the Apply specification must also implement the [Functor]!(#functor) specification.
v['fantasy-land/ap']!(u['fantasy-land/ap']!(a['fantasy-land/map']!(f => g => x => f(g(x)))))is equivalent tov['fantasy-land/ap']!(u)['fantasy-land/ap']!(a)(composition)
fantasy-land/ap method
Section titled “fantasy-land/ap method”fantasy-land/ap :: Apply f => f a ~> f (a -> b) -> f bA value which has an Apply must provide a fantasy-land/ap method. The fantasy-land/ap
method takes one argument:
a['fantasy-land/ap']!(b)-
bmust be an Apply of a function- If
bdoes not represent a function, the behaviour offantasy-land/apis unspecified. bmust be same Apply asa.
- If
-
amust be an Apply of any value -
fantasy-land/apmust apply the function in Applybto the value in Applya- No parts of return value of that function should be checked.
-
The
Applyreturned byfantasy-land/apmust be the same asaandb
Applicative
Section titled “Applicative”A value that implements the Applicative specification must also implement the [Apply]!(#apply) specification.
v['fantasy-land/ap']!(A['fantasy-land/of']!(x => x))is equivalent tov(identity)A['fantasy-land/of']!(x)['fantasy-land/ap']!(A['fantasy-land/of']!(f))is equivalent toA['fantasy-land/of']!(f(x))(homomorphism)A['fantasy-land/of']!(y)['fantasy-land/ap']!(u)is equivalent tou['fantasy-land/ap']!(A['fantasy-land/of']!(f => f(y)))(interchange)
fantasy-land/of method
Section titled “fantasy-land/of method”fantasy-land/of :: Applicative f => a -> f aA value which has an Applicative must provide a fantasy-land/of function on its
[type representative]!(#type-representatives). The fantasy-land/of function takes
one argument:
F['fantasy-land/of']!(a)Given a value f, one can access its type representative via the
constructor property:
f.constructor['fantasy-land/of']!(a)-
fantasy-land/ofmust provide a value of the same Applicative- No parts of
ashould be checked
- No parts of
A value that implements the Alt specification must also implement the [Functor]!(#functor) specification.
a['fantasy-land/alt']!(b)['fantasy-land/alt']!(c)is equivalent toa['fantasy-land/alt']!(b['fantasy-land/alt']!(c))(associativity)a['fantasy-land/alt']!(b)['fantasy-land/map']!(f)is equivalent toa['fantasy-land/map']!(f)['fantasy-land/alt']!(b['fantasy-land/map']!(f))(distributivity)
fantasy-land/alt method
Section titled “fantasy-land/alt method”fantasy-land/alt :: Alt f => f a ~> f a -> f aA value which has a Alt must provide a fantasy-land/alt method. The
fantasy-land/alt method takes one argument:
a['fantasy-land/alt']!(b)-
bmust be a value of the same Alt- If
bis not the same Alt, behaviour offantasy-land/altis unspecified. aandbcan contain any value of same type.- No parts of
a’s andb’s containing value should be checked.
- If
-
fantasy-land/altmust return a value of the same Alt.
A value that implements the Plus specification must also implement the [Alt]!(#alt) specification.
x['fantasy-land/alt']!(A['fantasy-land/zero']!())is equivalent tox(right identity)A['fantasy-land/zero']!()['fantasy-land/alt']!(x)is equivalent tox(left identity)A['fantasy-land/zero']!()['fantasy-land/map']!(f)is equivalent toA['fantasy-land/zero']!()(annihilation)
fantasy-land/zero method
Section titled “fantasy-land/zero method”fantasy-land/zero :: Plus f => () -> f aA value which has a Plus must provide a fantasy-land/zero function on its
[type representative]!(#type-representatives):
A['fantasy-land/zero']!()Given a value x, one can access its type representative via the
constructor property:
x.constructor['fantasy-land/zero']!()fantasy-land/zeromust return a value of the same Plus
Alternative
Section titled “Alternative”A value that implements the Alternative specification must also implement the [Applicative]!(#applicative) and [Plus]!(#plus) specifications.
x['fantasy-land/ap']!(f['fantasy-land/alt']!(g))is equivalent tox['fantasy-land/ap']!(f)['fantasy-land/alt']!(x['fantasy-land/ap']!(g))(distributivity)x['fantasy-land/ap']!(A['fantasy-land/zero']!())is equivalent toA['fantasy-land/zero']!()(annihilation)
Foldable
Section titled “Foldable”u['fantasy-land/reduce']is equivalent tou['fantasy-land/reduce']!((acc, x) => acc.concat([x]), []).reduce
fantasy-land/reduce method
Section titled “fantasy-land/reduce method”fantasy-land/reduce :: Foldable f => f a ~> ((b, a) -> b, b) -> bA value which has a Foldable must provide a fantasy-land/reduce method. The fantasy-land/reduce
method takes two arguments:
u['fantasy-land/reduce']!(f, x)-
fmust be a binary function- if
fis not a function, the behaviour offantasy-land/reduceis unspecified. - The first argument to
fmust be the same type asx. fmust return a value of the same type asx.- No parts of
f’s return value should be checked.
- if
-
xis the initial accumulator value for the reduction- No parts of
xshould be checked.
- No parts of
Traversable
Section titled “Traversable”A value that implements the Traversable specification must also implement the [Functor]!(#functor) and [Foldable]!(#foldable) specifications.
-
t(u['fantasy-land/traverse']!(F, x => x))is equivalent tou['fantasy-land/traverse']!(G, t)for anytsuch thatt(a)['fantasy-land/map']!(f)is equivalent tot(a['fantasy-land/map']!(f))(naturality) -
u['fantasy-land/traverse']!(F, F['fantasy-land/of'])is equivalent toF['fantasy-land/of']!(u)for any ApplicativeF(identity) -
u['fantasy-land/traverse']!(Compose, x => new Compose(x))is equivalent tonew Compose(u['fantasy-land/traverse']!(F, x => x)['fantasy-land/map']!(x => x['fantasy-land/traverse']!(G, x => x)))forComposedefined below and any ApplicativesFandG(composition)
function Compose(c) { this.c = c;}
Compose['fantasy-land/of'] = function(x) { return new Compose(F['fantasy-land/of']!(G['fantasy-land/of']!(x)));};
Compose.prototype['fantasy-land/ap'] = function(f) { return new Compose(this.c['fantasy-land/ap']!(f.c['fantasy-land/map']!(u => y => y['fantasy-land/ap']!(u))));};
Compose.prototype['fantasy-land/map'] = function(f) { return new Compose(this.c['fantasy-land/map']!(y => y['fantasy-land/map']!(f)));};fantasy-land/traverse method
Section titled “fantasy-land/traverse method”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:
u['fantasy-land/traverse']!(A, f)-
Amust be the [type representative]!(#type-representatives) of an Applicative. -
fmust be a function which returns a value- If
fis not a function, the behaviour offantasy-land/traverseis unspecified. fmust return a value of the type represented byA.
- If
-
fantasy-land/traversemust return a value of the type represented byA.
A value that implements the Chain specification must also implement the [Apply]!(#apply) specification.
m['fantasy-land/chain']!(f)['fantasy-land/chain']!(g)is equivalent tom['fantasy-land/chain']!(x => f(x)['fantasy-land/chain']!(g))(associativity)
fantasy-land/chain method
Section titled “fantasy-land/chain method”fantasy-land/chain :: Chain m => m a ~> (a -> m b) -> m bA value which has a Chain must provide a fantasy-land/chain method. The fantasy-land/chain
method takes one argument:
m['fantasy-land/chain']!(f)-
fmust be a function which returns a value- If
fis not a function, the behaviour offantasy-land/chainis unspecified. fmust return a value of the same Chain
- If
-
fantasy-land/chainmust return a value of the same Chain
ChainRec
Section titled “ChainRec”A value that implements the ChainRec specification must also implement the [Chain]!(#chain) specification.
M['fantasy-land/chainRec']!((next, done, v) => p(v) ? d(v)['fantasy-land/map']!(done) : n(v)['fantasy-land/map']!(next), i)is equivalent to(function step(v) { return p(v) ? d(v) : n(v)['fantasy-land/chain']!(step); }(i))(equivalence)- Stack usage of
M['fantasy-land/chainRec']!(f, i)must be at most a constant multiple of the stack usage offitself.
fantasy-land/chainRec method
Section titled “fantasy-land/chainRec method”fantasy-land/chainRec :: ChainRec m => ((a -> c, b -> c, a) -> m c, a) -> m bA Type which has a ChainRec must provide a fantasy-land/chainRec function on its
[type representative]!(#type-representatives). The fantasy-land/chainRec function
takes two arguments:
M['fantasy-land/chainRec']!(f, i)Given a value m, one can access its type representative via the
constructor property:
m.constructor['fantasy-land/chainRec']!(f, i)fmust be a function which returns a value- If
fis not a function, the behaviour offantasy-land/chainRecis unspecified. ftakes three argumentsnext,done,valuenextis a function which takes one argument of same type asiand can return any valuedoneis a function which takes one argument and returns the same type as the return value ofnextvalueis some value of the same type asi
fmust return a value of the same ChainRec which contains a value returned from eitherdoneornext
- If
fantasy-land/chainRecmust return a value of the same ChainRec which contains a value of same type as argument ofdone
A value that implements the Monad specification must also implement the [Applicative]!(#applicative) and [Chain]!(#chain) specifications.
M['fantasy-land/of']!(a)['fantasy-land/chain']!(f)is equivalent tof(a)(left identity)m['fantasy-land/chain']!(M['fantasy-land/of'])is equivalent tom(right identity)
Extend
Section titled “Extend”A value that implements the Extend specification must also implement the [Functor]!(#functor) specification.
w['fantasy-land/extend']!(g)['fantasy-land/extend']!(f)is equivalent tow['fantasy-land/extend']!(_w => f(_w['fantasy-land/extend']!(g)))
fantasy-land/extend method
Section titled “fantasy-land/extend method”fantasy-land/extend :: Extend w => w a ~> (w a -> b) -> w bAn Extend must provide a fantasy-land/extend method. The fantasy-land/extend
method takes one argument:
w['fantasy-land/extend']!(f)-
fmust be a function which returns a value- If
fis not a function, the behaviour offantasy-land/extendis unspecified. fmust return a value of typev, for some variablevcontained inw.- No parts of
f’s return value should be checked.
- If
-
fantasy-land/extendmust return a value of the same Extend.
Comonad
Section titled “Comonad”A value that implements the Comonad specification must also implement the [Extend]!(#extend) specification.
w['fantasy-land/extend']!(_w => _w['fantasy-land/extract']!())is equivalent tow(left identity)w['fantasy-land/extend']!(f)['fantasy-land/extract']!()is equivalent tof(w)(right identity)
fantasy-land/extract method
Section titled “fantasy-land/extract method”fantasy-land/extract :: Comonad w => w a ~> () -> aA value which has a Comonad must provide a fantasy-land/extract method on itself.
The fantasy-land/extract method takes no arguments:
w['fantasy-land/extract']!()fantasy-land/extractmust return a value of typev, for some variablevcontained inw.vmust have the same type thatfreturns infantasy-land/extend.
Bifunctor
Section titled “Bifunctor”A value that implements the Bifunctor specification must also implement the [Functor]!(#functor) specification.
p['fantasy-land/bimap']!(a => a, b => b)is equivalent top(identity)p['fantasy-land/bimap']!(a => f(g(a)), b => h(i(b)))is equivalent top['fantasy-land/bimap']!(g, i)['fantasy-land/bimap']!(f, h)(composition)
fantasy-land/bimap method
Section titled “fantasy-land/bimap method”fantasy-land/bimap :: Bifunctor f => f a c ~> (a -> b, c -> d) -> f b dA value which has a Bifunctor must provide a fantasy-land/bimap method. The fantasy-land/bimap
method takes two arguments:
c['fantasy-land/bimap']!(f, g)-
fmust be a function which returns a value- If
fis not a function, the behaviour offantasy-land/bimapis unspecified. fcan return any value.- No parts of
f’s return value should be checked.
- If
-
gmust be a function which returns a value- If
gis not a function, the behaviour offantasy-land/bimapis unspecified. gcan return any value.- No parts of
g’s return value should be checked.
- If
-
fantasy-land/bimapmust return a value of the same Bifunctor.
Profunctor
Section titled “Profunctor”A value that implements the Profunctor specification must also implement the [Functor]!(#functor) specification.
p['fantasy-land/promap']!(a => a, b => b)is equivalent top(identity)p['fantasy-land/promap']!(a => f(g(a)), b => h(i(b)))is equivalent top['fantasy-land/promap']!(f, i)['fantasy-land/promap']!(g, h)(composition)
fantasy-land/promap method
Section titled “fantasy-land/promap method”fantasy-land/promap :: Profunctor p => p b c ~> (a -> b, c -> d) -> p a dA value which has a Profunctor must provide a fantasy-land/promap method.
The fantasy-land/promap method takes two arguments:
c['fantasy-land/promap']!(f, g)-
fmust be a function which returns a value- If
fis not a function, the behaviour offantasy-land/promapis unspecified. fcan return any value.- No parts of
f’s return value should be checked.
- If
-
gmust be a function which returns a value- If
gis not a function, the behaviour offantasy-land/promapis unspecified. gcan return any value.- No parts of
g’s return value should be checked.
- If
-
fantasy-land/promapmust return a value of the same Profunctor
Derivations
Section titled “Derivations”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][] !:function equals(other) { return this['fantasy-land/lte']!(other) && other['fantasy-land/lte']!(this); } -
[
fantasy-land/map][] may be derived from [fantasy-land/ap][] and [fantasy-land/of][] !:function map(f) { return this['fantasy-land/ap']!(this.constructor['fantasy-land/of']!(f)); } -
[
fantasy-land/map][] may be derived from [fantasy-land/chain][] and [fantasy-land/of][] !:function map(f) { return this['fantasy-land/chain']!(a => this.constructor['fantasy-land/of']!(f(a))); } -
[
fantasy-land/map][] may be derived from [fantasy-land/bimap][] !:function map(f) { return this['fantasy-land/bimap']!(a => a, f); } -
[
fantasy-land/map][] may be derived from [fantasy-land/promap][] !:function map(f) { return this['fantasy-land/promap']!(a => a, f); } -
[
fantasy-land/ap][] may be derived from [fantasy-land/chain][] !:function ap(m) { return m['fantasy-land/chain']!(f => this['fantasy-land/map']!(f)); } -
[
fantasy-land/reduce][] may be derived as follows: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']!(x => new Const(x), Const['fantasy-land/of']).value;} -
[
fantasy-land/map][] may be derived as follows: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']!(x => Id['fantasy-land/of']!(f(x)), Id['fantasy-land/of']).value;} -
[
fantasy-land/filter][] may be derived from [fantasy-land/of][], [fantasy-land/chain][], and [fantasy-land/zero][] !:function filter(pred) {var F = this.constructor;return this['fantasy-land/chain']!(x => pred(x) ? F['fantasy-land/of']!(x) : F['fantasy-land/zero']!());} -
[
fantasy-land/filter][] may be derived from [fantasy-land/concat][], [fantasy-land/of][], [fantasy-land/zero][], and [fantasy-land/reduce][] !:function filter(pred) {var F = this.constructor;return this['fantasy-land/reduce']!((f, x) => pred(x) ? f['fantasy-land/concat']!(F['fantasy-land/of']!(x)) : 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).
- 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.
- It’s discouraged to overload the specified methods. It can easily result in broken and buggy behaviour.
- It is recommended to throw an exception on unspecified behaviour.
- An
Identitycontainer which implements many of the methods is provided by sanctuary-identity.
[fantasy-land/ap] !: #ap-method
[fantasy-land/bimap] !: #bimap-method
[fantasy-land/chain] !: #chain-method
[fantasy-land/concat] !: #concat-method
[fantasy-land/equals] !: #equals-method
[fantasy-land/filter] !: #filter-method
[fantasy-land/lte] !: #lte-method
[fantasy-land/map] !: #map-method
[fantasy-land/of] !: #of-method
[fantasy-land/promap] !: #promap-method
[fantasy-land/reduce] !: #reduce-method
[fantasy-land/zero] !: #zero-method
Alternatives
Section titled “Alternatives”There also exists Static Land Specification with exactly the same ideas as Fantasy Land but based on static methods instead of instance methods.