diff --git a/README.md b/README.md index 2fdeb8c..23c05dd 100644 --- a/README.md +++ b/README.md @@ -7,13 +7,17 @@ This project specifies interoperability of common algebraic structures: +* Setoid * Semigroup * Monoid * Functor +* Apply * Applicative * Chain * Monad +![](figures/dependencies.png) + ## General An algebra is a set of values, a set of operators that it is closed @@ -37,6 +41,26 @@ implemented and how they can be derived from new methods. ## Algebras +### Setoid + +1. `a.equals(a) === true` (reflexivity) +2. `a.equals(b) === b.equals(a)` (symmetry) +3. If `a.equals(b)` and `b.equals(c)`, then `a.equals(c)` (transitivity) + +#### `equals` method + +A value which has a Setoid must provide an `equals` method. The +`equals` method takes one argument: + + a.equals(b) + +1. `b` must be a value of the same Setoid + + 1. If `b` is not the same Setoid, behaviour of `equals` is + unspecified (returning `false` is recommended). + +2. `equals` must return a boolean (`true` or `false`). + ### Semigroup 1. `a.concat(b).concat(c)` is equivalent to `a.concat(b.concat(c))` (associativity) @@ -93,37 +117,43 @@ method takes one argument: 2. `map` must return a value of the same Functor -### Applicative +### Apply -A value that implements the Applicative specification must also +A value that implements the Apply specification must also implement the Functor specification. -A value which satisfies the specification of a Applicative does not -need to implement: - -* Functor's `map`; derivable as `function(f) { return this.of(f).ap(this); })}` - -1. `a.of(function(a) { return a; }).ap(v)` is equivalent to `v` (identity) -2. `a.of(function(f) { return function(g) { return function(x) { return f(g(x))}; }; }).ap(u).ap(v).ap(w)` is equivalent to `u.ap(v.ap(w))` (composition) -3. `a.of(f).ap(a.of(x))` is equivalent to `a.of(f(x))` (homomorphism) -4. `u.ap(a.of(y))` is equivalent to `a.of(function(f) { return f(y); }).ap(u)` (interchange) +1. `a.map(function(f) { return function(g) { return function(x) { return f(g(x))}; }; }).ap(u).ap(v)` is equivalent to `a.ap(u.ap(v))` (composition) #### `ap` method -A value which has an Applicative must provide an `ap` method. The `ap` +A value which has an Apply must provide an `ap` method. The `ap` method takes one argument: a.ap(b) -1. `a` must be an Applicative of a function, +1. `a` must be an Apply of a function, 1. If `a` does not represent a function, the behaviour of `ap` is unspecified. -2. `b` must be an Applicative of any value +2. `b` must be an Apply of any value + +3. `ap` must apply the function in Apply `a` to the value in + Apply `b` + +### Applicative + +A value that implements the Applicative specification must also +implement the Apply specification. -3. `ap` must apply the function in Applicative `a` to the value in - Applicative `b` +A value which satisfies the specification of an Applicative does not +need to implement: + +* Functor's `map`; derivable as `function(f) { return this.of(f).ap(this); })}` + +1. `a.of(function(a) { return a; }).ap(v)` is equivalent to `v` (identity) +2. `a.of(f).ap(a.of(x))` is equivalent to `a.of(f(x))` (homomorphism) +3. `u.ap(a.of(y))` is equivalent to `a.of(function(f) { return f(y); }).ap(u)` (interchange) #### `of` method @@ -139,6 +169,14 @@ or its `constructor` object. The `of` method takes one argument: ### Chain +A value that implements the Chain specification must also +implement the Apply specification. + +A value which satisfies the specification of a Chain does not +need to implement: + +* Apply's `ap`; derivable as `m.chain(function(f) { return m.map(f); })` + 1. `m.chain(f).chain(g)` is equivalent to `m.chain(function(x) { return f(x).chain(g); })` (associativity) #### `chain` method @@ -164,7 +202,7 @@ the Applicative and Chain specifications. A value which satisfies the specification of a Monad does not need to implement: -* Applicative's `ap`; derivable as `function(m) { return this.chain(function(f) { return m.map(f); }); }` +* Apply's `ap`; derivable as `function(m) { return this.chain(function(f) { return m.map(f); }); }` * Functor's `map`; derivable as `function(f) { var m = this; return m.chain(function(a) { return m.of(f(a)); })}` 1. `m.of(a).chain(f)` is equivalent to `f(a)` (left identity) diff --git a/figures/dependencies.mp b/figures/dependencies.mp new file mode 100644 index 0000000..1b8709a --- /dev/null +++ b/figures/dependencies.mp @@ -0,0 +1,67 @@ +prologues := 3; +outputtemplate := "%j.svg"; +outputformat := "svg"; + +dimX=2.5cm; +dimY=-1.5cm; + +vardef clippath(expr p, a, b) = + save q, s, t, r; + + numeric s; + path q[]; + numeric t; + path r; + + q1 = bbox(a); + q2 = bbox(b); + + (whatever, t1) = q1 intersectiontimes p; + (whatever, t2) = q2 intersectiontimes (reverse p); + + subpath (t1, length(p) - t2) of p +enddef; + +def midpoint(expr p) = point 0.5 of p enddef; + +beginfig(1); +picture q[]; + +z1=(0dimX, 0dimY); +z2=(0dimX, 1dimY); + +z3=(1dimX, 0dimY); +z4=(1dimX, 1dimY); +z5=(2dimX, 1dimY); + +z6=(1dimX, 2dimY); +z7=(2dimX, 2dimY); + +% domain labels +q1 = thelabel("Semigroup", z1); +q2 = thelabel("Monoid", z2); + +q3 = thelabel("Functor", z3); + +q4 = thelabel("Apply", z4); +q5 = thelabel("Applicative", z5); + +q6 = thelabel("Chain", z6); +q7 = thelabel("Monad", z7); + +for i=1 upto 7: + draw q[i]; +endfor + +% arrows + +drawarrow clippath(z1..z2, q1, q2); % Semigroup -> Monoid + +drawarrow clippath(z3..z4, q3, q4); % Functor -> Apply +drawarrow clippath(z4..z5, q4, q5); % Apply -> Applicative +drawarrow clippath(z4..z6, q4, q6); % Apply -> Chain +drawarrow clippath(z5..z7, q5, q7); % Applicative -> Monad +drawarrow clippath(z6..z7, q6, q7); % Chain -> Monad + +endfig; +end diff --git a/figures/dependencies.png b/figures/dependencies.png new file mode 100644 index 0000000..c065453 Binary files /dev/null and b/figures/dependencies.png differ diff --git a/figures/dependencies.svg b/figures/dependencies.svg new file mode 100644 index 0000000..6e50663 --- /dev/null +++ b/figures/dependencies.svg @@ -0,0 +1,147 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/id.js b/id.js index faa7fdc..e8099cf 100644 --- a/id.js +++ b/id.js @@ -2,6 +2,11 @@ function Id(a) { this.value = a; } +// Setoid +Id.prototype.equals = function (b) { + return typeof this.value.equals === "function" ? this.value.equals(b.value) : this.value === b.value; +}; + // Semigroup (value must also be a Semigroup) Id.prototype.concat = function(b) { return new Id(this.value.concat(b.value)); diff --git a/implementations.md b/implementations.md index 218da32..70f40fb 100644 --- a/implementations.md +++ b/implementations.md @@ -20,7 +20,10 @@ Here are a list of implementations that live in Fantasy Land: Promises that implements Semigroup, Monoid, Functor, Applicative, Chain and Monad * [Pirandello](https://github.com/quarterto/Pirandello) better streams, with a MonadPlus - +* [Parsimmon](https://github.com/jayferd/parsimmon) implements parsers that are semigroups, applicative functors, and monads. +* [Bennu](https://github.com/mattbierner/bennu/) parsec style parser combinators implement monad, monoid, functor, and applicative functor. +* [Akh](https://github.com/mattbierner/akh/) is a collection of monad transformers and common structures that implement Fantasy Land interfaces. + Conforming implementations are encouraged to promote the Fantasy Land logo: ![](logo.png) diff --git a/package.json b/package.json index 1d5e7e8..35b4f0f 100644 --- a/package.json +++ b/package.json @@ -1,23 +1,28 @@ { - "name": "fantasy-land", - "author": "Brian McKenna", - "version": "0.0.1", - "description": "Specification for interoperability of common algebraic structures in JavaScript", - "license": "XXX", - "homepage": "https://github.com/pufuwozu/fantasy-land", - "keywords": [ - "algebraic", - "monad", - "functor", - "monoid", - "semigroup" - ], - "issues": { - "url": "https://github.com/pufuwozu/fantasy-land/issues" - }, - "repository": { - "type": "git", - "url": "https://github.com/pufuwozu/fantasy-land.git" - }, - "files": ["id.js"] + "name": "fantasy-land", + "author": "Brian McKenna", + "version": "0.1.0", + "description": "Specification for interoperability of common algebraic structures in JavaScript", + "license": "XXX", + "homepage": "https://github.com/fantasyland/fantasy-land", + "keywords": [ + "algebraic", + "monad", + "applicative", + "functor", + "monoid", + "semigroup", + "chain", + "apply" + ], + "issues": { + "url": "https://github.com/fantasyland/fantasy-land/issues" + }, + "repository": { + "type": "git", + "url": "https://github.com/fantasyland/fantasy-land.git" + }, + "files": [ + "id.js" + ] }