Skip to content

Commit

Permalink
Merge pull request #220 from davidchambers/traverse
Browse files Browse the repository at this point in the history
have traverse take a type representative rather than an "of" function
  • Loading branch information
davidchambers authored Jan 17, 2017
2 parents e0088f1 + 498e823 commit b7e5d18
Show file tree
Hide file tree
Showing 5 changed files with 45 additions and 35 deletions.
26 changes: 15 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -340,13 +340,15 @@ method takes two arguments:
A value that implements the Traversable specification must also
implement the [Functor](#functor) and [Foldable](#foldable) specifications.

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)
1. `t(u.traverse(F, x => x))` is equivalent to `u.traverse(G, t)` for any
`t` such that `t(a).map(f)` is equivalent to `t(a.map(f))` (naturality)

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

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)
3. `u.traverse(Compose, x => new Compose(x))` is equivalent to
`new Compose(u.traverse(F, x => x).map(x => x.traverse(G, x => x)))` for
`Compose` defined below and any Applicatives `F` and `G` (composition)

```js
var Compose = function(c) {
Expand All @@ -369,22 +371,24 @@ Compose.prototype.map = function(f) {
#### `traverse` method

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

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

u.traverse(f, of)
u.traverse(A, f)

1. `f` must be a function which returns a value
1. `A` must be the [type representative](#type-representatives) of an
Applicative.

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

1. If `f` is not a function, the behaviour of `traverse` is
unspecified.
2. `f` must return a value of an Applicative
2. `f` must return a value of the type represented by `A`.

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
3. `traverse` must return a value of the type represented by `A`.

### Chain

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

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

Expand Down
14 changes: 9 additions & 5 deletions internal/patch.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,23 @@ module.exports = () => {
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.map] = function(f) {
return this.map(x => f(x));
};
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.reduce] = function(f, x) {
return this.reduce((x, y) => f(x, y), x);
};
Array.prototype[fl.concat] = Array.prototype.concat;
Array.prototype[fl.traverse] = function(f, p) {
return this.map(f)[fl.reduce](
Array.prototype[fl.traverse] = function(typeRep, f) {
return this[fl.map](f)[fl.reduce](
(ys, x) => ys[fl.ap](x[fl.map](y => z => z[fl.concat](y))),
p([])
typeRep[fl.of]([])
);
};
Array.prototype[fl.alt] = function(b) {
Expand Down
30 changes: 16 additions & 14 deletions laws/traversable.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,33 +8,35 @@ const {of, traverse, map} = require('..');
### Traversable
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)
1. `t(u.traverse(F, x => x))` is equivalent to `u.traverse(G, t)` for any
`t` such that `t(a).map(f)` is equivalent to `t(a.map(f))` (naturality)
2. `u.traverse(F.of, F.of)` is equivalent to `F.of(u)` for any Applicative `F` (identity)
2. `u.traverse(F, F.of)` is equivalent to `F.of(u)` for any Applicative `F`
(identity)
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)
3. `u.traverse(Compose, x => new Compose(x))` is equivalent to
`new Compose(u.traverse(F, x => x).map(x => x.traverse(G, x => x)))` for
`Compose` defined below and any Applicatives `F` and `G` (composition)
**/

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

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

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

const composition = t => eq => x => {
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])));
const composition = T => t => eq => x => {
const a = t(x)[traverse](Compose, Compose);
const b = Compose(t(x)[traverse](Id, y => y)[map](x => x[traverse](Id, y => y)));
return eq(a, b);
};

Expand Down
6 changes: 3 additions & 3 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ exports.setoid = {
};

exports.traversable = {
naturality: test(x => traversable.naturality(Id[fl.of])(equality)(Id[fl.of](x))),
identity: test(traversable.identity(Id[fl.of])(equality)),
composition: test(x => traversable.composition(Id[fl.of])(equality)(Id[fl.of](Sum[fl.of](x)))),
naturality: test(x => traversable.naturality(Id)(Id[fl.of])(equality)(Id[fl.of](x))),
identity: test(traversable.identity(Id)(equality)),
composition: test(x => traversable.composition(Id)(Id[fl.of])(equality)(Id[fl.of](Sum[fl.of](x)))),
};

0 comments on commit b7e5d18

Please sign in to comment.