Skip to content

Commit

Permalink
Use traverse() as base method for Traversable (#135)
Browse files Browse the repository at this point in the history
* use traverse() as base method for Traversable
  • Loading branch information
rpominov authored and SimonRichardson committed Sep 14, 2016
1 parent d954e98 commit df561b9
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 28 deletions.
49 changes: 38 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -251,13 +251,13 @@ method takes two arguments:
A value that implements the Traversable specification must also
implement the Functor and Foldable specifications.

1. `t(u.sequence(f.of))` is equivalent to `u.map(t).sequence(g.of)`
1. `t(u.traverse(x => x, F.of))` is equivalent to `u.traverse(t, G.of)`
for any `t` such that `t(a).map(f)` is equivalent to `t(a.map(f))` (naturality)

2. `u.map(F.of).sequence(F.of)` is equivalent to `F.of(u)` for any Applicative `F` (identity)
2. `u.traverse(F.of, F.of)` is equivalent to `F.of(u)` for any Applicative `F` (identity)

3. `u.map(x => new Compose(x)).sequence(Compose.of)` is equivalent to
`new Compose(u.sequence(F.of).map(v => v.sequence(G.of)))` for `Compose` defined below and any Applicatives `F` and `G` (composition)
3. `u.traverse(x => new Compose(x), Compose.of)` is equivalent to
`new Compose(u.traverse(x => x, F.of).map(x => x.traverse(x => x, G.of)))` for `Compose` defined below and any Applicatives `F` and `G` (composition)

```js
var Compose = function(c) {
Expand All @@ -277,18 +277,25 @@ Compose.prototype.map = function(f) {
};
```

#### `sequence` method
#### `traverse` method

```hs
sequence :: Apply f, Traversable t => t (f a) ~> (b -> f b) -> f (t a)
traverse :: Apply f, Traversable t => t a ~> ((a -> f b), (c -> f c)) -> f (t b)
```

A value which has a Traversable must provide a `sequence` method. The `sequence`
method takes one argument:
A value which has a Traversable must provide a `traverse` method. The `traverse`
method takes two arguments:

u.traverse(f, of)

1. `f` must be a function which returns a value

u.sequence(of)
1. If `f` is not a function, the behaviour of `traverse` is
unspecified.
2. `f` must return a value of an Applicative

1. `of` must return the Applicative that `u` contains.
2. `of` must be the `of` method of the Applicative that `f` returns
3. `traverse` must return a value of the same Applicative that `f` returns

### Chain

Expand Down Expand Up @@ -513,7 +520,27 @@ to implement certain methods then derive the remaining methods. Derivations:
Const.prototype.ap = function(b) {
return new Const(f(b.value, this.value));
};
return this.map(x => new Const(x)).sequence(Const.of).value;
return this.traverse(x => new Const(x), Const.of).value;
}
```

- [`map`][] may be derived as follows:

```js
function(f) {
function Id(value) {
this.value = value;
};
Id.of = function(x) {
return new Id(x);
};
Id.prototype.map = function(f) {
return new Id(f(this.value));
};
Id.prototype.ap = function(b) {
return new Id(this.value(b.value));
};
return this.traverse(x => Id.of(f(x)), Id.of).value;
}
```

Expand Down
4 changes: 2 additions & 2 deletions id.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ Id.prototype[fl.ap] = function(b) {
};

// Traversable
Id.prototype[fl.sequence] = function(of) {
Id.prototype[fl.traverse] = function(f, of) {
// the of argument is only provided for types where map might fail.
return this.value[fl.map](Id[fl.of]);
return f(this.value)[fl.map](Id[fl.of]);
};

// Chain
Expand Down
2 changes: 1 addition & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
ap: 'fantasy-land/ap',
of: 'fantasy-land/of',
reduce: 'fantasy-land/reduce',
sequence: 'fantasy-land/sequence',
traverse: 'fantasy-land/traverse',
chain: 'fantasy-land/chain',
chainRec: 'fantasy-land/chainRec',
extend: 'fantasy-land/extend',
Expand Down
28 changes: 14 additions & 14 deletions laws/traversable.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

const Id = require('../id');
const {identity} = require('fantasy-combinators');
const {of, ap, reduce, sequence, map, equals, empty, concat} = require('..');
const {of, ap, reduce, traverse, map, equals, empty, concat} = require('..');
const {tagged} = require('daggy');

const Compose = tagged('c');
Expand All @@ -23,43 +23,43 @@ Array.prototype[equals] = function(y) {
Array.prototype[map] = Array.prototype.map
Array.prototype[reduce] = Array.prototype.reduce
Array.prototype[concat] = Array.prototype.concat
Array.prototype[sequence] = function(p) {
return this[reduce]((ys, x) => {
return ys[ap](identity(x)[map](y => z => z[concat](y)));
Array.prototype[traverse] = function(f, p) {
return this.map(f)[reduce]((ys, x) => {
return ys[ap](x[map](y => z => z[concat](y)));
}, p([]));
};

/*
### Traversable
1. `t(u.sequence(f.of))` is equivalent to `u.map(t).sequence(g.of)`
where `t` is a natural transformation from `f` to `g` (naturality)
1. `t(u.traverse(x => x, F.of))` is equivalent to `u.traverse(t, G.of)`
for any `t` such that `t(a).map(f)` is equivalent to `t(a.map(f))` (naturality)
2. `u.map(x => Id(x)).sequence(Id.of)` is equivalent to `Id.of` (identity)
2. `u.traverse(F.of, F.of)` is equivalent to `F.of(u)` for any Applicative `F` (identity)
3. `u.map(Compose).sequence(Compose.of)` is equivalent to
`Compose(u.sequence(f.of).map(x => x.sequence(g.of)))` (composition)
3. `u.traverse(x => new Compose(x), Compose.of)` is equivalent to
`new Compose(u.traverse(x => x, F.of).map(x => x.traverse(x => x, G.of)))` for `Compose` defined below and any Applicatives `F` and `G` (composition)
*/

const naturality = t => eq => x => {
const a = identity(t(x)[sequence](t[of]));
const b = t(x)[map](identity)[sequence](t[of]);
const a = identity(t(x)[traverse](y => y, t[of]));
const b = t(x)[traverse](identity, t[of]);
return eq(a, b);
};

const identityʹ = t => eq => x => {
const u = [x];

const a = u[map](Id[of])[sequence](Id[of]);
const a = u[traverse](Id[of], Id[of]);
const b = Id[of](u);
return eq(a, b);
};

const composition = t => eq => x => {
const a = t(x)[map](Compose)[sequence](Compose[of]);
const b = Compose(t(x)[sequence](Id[of])[map](x => x[sequence](Id[of])));
const a = t(x)[traverse](Compose, Compose[of]);
const b = Compose(t(x)[traverse](y => y, Id[of])[map](x => x[traverse](y => y, Id[of])));
return eq(a, b);
};

Expand Down

0 comments on commit df561b9

Please sign in to comment.