Skip to content

Commit

Permalink
Add Alt, Plus and Alternative specs (#197)
Browse files Browse the repository at this point in the history
- refactor tests
- 'id.js' moved to 'internal/'
  • Loading branch information
safareli authored and davidchambers committed Nov 4, 2016
1 parent 79ff3a3 commit 9ca844c
Show file tree
Hide file tree
Showing 18 changed files with 374 additions and 154 deletions.
70 changes: 68 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ structures:
* [Functor](#functor)
* [Apply](#apply)
* [Applicative](#applicative)
* [Alt](#alt)
* [Plus](#plus)
* [Alternative](#alternative)
* [Foldable](#foldable)
* [Traversable](#traversable)
* [Chain](#chain)
Expand All @@ -25,7 +28,7 @@ structures:
* [Bifunctor](#bifunctor)
* [Profunctor](#profunctor)

<img src="figures/dependencies.png" width="863" height="347" />
<img src="figures/dependencies.png" width="888" height="340" />

## General

Expand Down Expand Up @@ -243,6 +246,69 @@ Given a value `f`, one can access its type representative via the

1. No parts of `a` should be checked

### Alt

A value that implements the Alt specification must also implement
the [Functor](#functor) specification.

1. `a.alt(b).alt(c)` is equivalent to `a.alt(b.alt(c))` (associativity)
2. `a.alt(b).map(f)` is equivalent to `a.map(f).alt(b.map(f))` (distributivity)

#### `alt` method

```hs
alt :: Alt f => f a ~> f a -> f a
```

A value which has a Alt must provide a `alt` method. The
`alt` method takes one argument:

a.alt(b)

1. `b` must be a value of the same Alt

1. If `b` is not the same Alt, behaviour of `alt` is
unspecified.
2. `a` and `b` can contain any value of same type.
3. No parts of `a`'s and `b`'s containing value should be checked.

2. `alt` must return a value of the same Alt.

### Plus

A value that implements the Plus specification must also implement
the [Alt](#alt) specification.

1. `x.alt(A.zero())` is equivalent to `x` (right identity)
2. `A.zero().alt(x)` is equivalent to `x` (left identity)
2. `A.zero().map(f)` is equivalent to `A.zero()` (annihilation)

#### `zero` method

```hs
zero :: Plus f => () -> f a
```

A value which has a Plus must provide an `zero` function on its
[type representative](#type-representatives):

A.zero()

Given a value `x`, one can access its type representative via the
`constructor` property:

x.constructor.zero()

1. `zero` must return a value of the same Plus

### Alternative

A value that implements the Alternative specification must also implement
the [Applicative](#applicative) and [Plus](#plus) specifications.

1. `x.ap(f.alt(g))` is equivalent to `x.ap(f).alt(x.ap(g))` (distributivity)
1. `x.ap(A.zero())` is equivalent to `A.zero()` (annihilation)

### Foldable

1. `u.reduce` is equivalent to `u.reduce((acc, x) => acc.concat([x]), []).reduce`
Expand Down Expand Up @@ -589,7 +655,7 @@ be equivalent to that of the derivation (or derivations).
result in broken and buggy behaviour.
3. It is recommended to throw an exception on unspecified behaviour.
4. An `Id` container which implements many of the methods is provided in
`id.js`.
`internal/id.js`.


[`ap`]: #ap-method
Expand Down
2 changes: 1 addition & 1 deletion bower.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"/node_modules/",
"/.*",
"/id.js",
"/id_test.js",
"/test.js",
"/implementations.md",
"/logo.png",
"/package.json"
Expand Down
7 changes: 7 additions & 0 deletions figures/dependencies.dot
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ digraph {
node [shape=plaintext]

# Algebras
Alt;
Alternative;
Applicative;
Apply;
Bifunctor;
Expand All @@ -13,23 +15,28 @@ digraph {
Functor;
Monad;
Monoid;
Plus;
Profunctor;
Semigroup;
Setoid;
Traversable;

# Dependencies
Alt -> Plus;
Applicative -> Alternative;
Applicative -> Monad;
Apply -> Applicative;
Apply -> Chain;
Chain -> ChainRec;
Chain -> Monad;
Extend -> Comonad;
Foldable -> Traversable;
Functor -> Alt;
Functor -> Apply;
Functor -> Bifunctor;
Functor -> Extend;
Functor -> Profunctor;
Functor -> Traversable;
Plus -> Alternative;
Semigroup -> Monoid;
}
Binary file modified figures/dependencies.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
113 changes: 0 additions & 113 deletions id_test.js

This file was deleted.

2 changes: 2 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
map: 'fantasy-land/map',
ap: 'fantasy-land/ap',
of: 'fantasy-land/of',
alt: 'fantasy-land/alt',
zero: 'fantasy-land/zero',
reduce: 'fantasy-land/reduce',
traverse: 'fantasy-land/traverse',
chain: 'fantasy-land/chain',
Expand Down
18 changes: 18 additions & 0 deletions internal/compose.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
'use strict';

const {tagged} = require('daggy');

const fl = require('..');
const {equality} = require('./func');

const Compose = module.exports = tagged('c');
Compose[fl.of] = Compose;
Compose.prototype[fl.ap] = function(f) {
return Compose(this.c[fl.ap](f.c[fl.map](u => y => y[fl.ap](u))));
};
Compose.prototype[fl.map] = function(f) {
return Compose(this.c[fl.map](y => y[fl.map](f)));
};
Compose.prototype[fl.equals] = function(x) {
return equality(this.c, x.c);
};
8 changes: 8 additions & 0 deletions internal/func.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
'use strict';

const fl = require('../');
const equality = (x, y) => typeof x[fl.equals] === 'function' ? x[fl.equals](y) : x === y;

module.exports = {
equality,
};
13 changes: 6 additions & 7 deletions id.js → internal/id.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
'use strict';

const fl = require('./');
const fl = require('../');
const {equality} = require('./func');

const Id = function Id(a) {
this.value = a;
};
const {tagged} = require('daggy');

const Id = module.exports = tagged('value');

// Setoid
Id.prototype[fl.equals] = function(b) {
return typeof this.value[fl.equals] === 'function' ? this.value[fl.equals](b.value) : this.value === b.value;
return equality(this.value, b.value);
};

// Semigroup (value must also be a Semigroup)
Expand Down Expand Up @@ -73,5 +74,3 @@ Id[fl.of] = function(a) {
Id.prototype[fl.extract] = function() {
return this.value;
};

module.exports = Id;
15 changes: 15 additions & 0 deletions internal/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
'use strict';

const patch = require('./patch');
const Id = require('./id');
const Sum = require('./string_sum');
const Compose = require('./compose');
const {equality} = require('./func');

module.exports = {
Id,
Sum,
Compose,
equality,
patch,
};
29 changes: 29 additions & 0 deletions internal/patch.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
'use strict';

const fl = require('..');

module.exports = () => {
String.prototype[fl.concat] = String.prototype.concat;
Array.prototype[fl.equals] = function(y) {
return this.length === y.length && this.join('') === y.join('');
};
Array.prototype[fl.map] = Array.prototype.map;
Array.prototype[fl.ap] = function(fs) {
return fs[fl.chain](f => this.map(f));
};
Array.prototype[fl.chain] = function(f) {
return [].concat(this.map(f));
};
Array.prototype[fl.reduce] = Array.prototype.reduce;
Array.prototype[fl.concat] = Array.prototype.concat;
Array.prototype[fl.traverse] = function(f, p) {
return this.map(f)[fl.reduce](
(ys, x) => ys[fl.ap](x[fl.map](y => z => z[fl.concat](y))),
p([])
);
};
Array.prototype[fl.alt] = function(b) {
return this.concat(b);
};
Array[fl.zero] = () => [];
};
20 changes: 20 additions & 0 deletions internal/string_sum.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
'use strict';

const {tagged} = require('daggy');

const fl = require('..');
const {equality} = require('./func');

// Special type of sum for the type of string.
const Sum = module.exports = tagged('v');
Sum[fl.of] = Sum;
Sum[fl.empty] = () => Sum('');
Sum.prototype[fl.map] = function(f) {
return Sum(f(this.v));
};
Sum.prototype[fl.concat] = function(x) {
return Sum(this.v + x.v);
};
Sum.prototype[fl.equals] = function(x) {
return equality(this.v, x.v);
};
Loading

0 comments on commit 9ca844c

Please sign in to comment.