Skip to content

Commit

Permalink
Add profunctor spec
Browse files Browse the repository at this point in the history
  • Loading branch information
scott-christopher committed Jul 18, 2016
1 parent dee6043 commit 30638f5
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 2 deletions.
38 changes: 37 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ structures:
* [Extend](#extend)
* [Comonad](#comonad)
* [Bifunctor](#bifunctor)
* [Profunctor](#profunctor)

<img src="figures/dependencies.png" width="677" height="212" />

Expand Down Expand Up @@ -376,6 +377,34 @@ method takes two arguments:

3. `bimap` must return a value of the same Bifunctor.

### Profunctor

A value that implements the Profunctor specification must also implement
the Functor specification.

1. `p.promap(a => a, b => b)` is equivalent to `p` (identity)
2. `p.promap(a => f(g(a)), b => h(i(b)))` is equivalent to `p.promap(f, i).promap(g, h)` (composition)

#### `promap` method

A value which has a Profunctor must provide a `promap` method.

The `profunctor` method takes two arguments:

c.promap(f, g)

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

1. If `f` is not a function, the behaviour of `promap` is unspecified.
2. `f` can return any value.

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

1. If `g` is not a function, the behaviour of `promap` is unspecified.
2. `g` can return any value.

3. `promap` must return a value of the same Profunctor

## Derivations

When creating data types which satisfy multiple algebras, authors may choose
Expand All @@ -392,13 +421,19 @@ to implement certain methods then derive the remaining methods. Derivations:
```js
function(f) { var m = this; return m.chain(a => m.of(f(a))); }
```

- [`map`][] may be derived from [`bimap`]:

```js
function(f) { return this.bimap(a => a, f); }
```

- [`map`][] may be derived from [`promap`]:

```js
function(f) { return this.promap(a => a, f) }
```

- [`ap`][] may be derived from [`chain`][]:

```js
Expand Down Expand Up @@ -450,5 +485,6 @@ be equivalent to that of the derivation (or derivations).
[`extract`]: #extract-method
[`map`]: #map-method
[`of`]: #of-method
[`promap`]: #promap-method
[`reduce`]: #reduce-method
[`sequence`]: #sequence-method
3 changes: 2 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ module.exports = {
chain: 'chain',
extend: 'extend',
extract: 'extract',
bimap: 'bimap'
bimap: 'bimap',
promap: 'promap'
}
28 changes: 28 additions & 0 deletions laws/profunctor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
'use strict';

const {identity, compose} = require('fantasy-combinators');
const {promap} = require('..');

/**
### Profunctor
1. `p.promap(a => a, b => b)` is equivalent to `p` (identity)
2. `p.promap(compose(f1)(f2), compose(g1)(g2))` is equivalent to `p.promap(f1, g1).promap(f2, g2)` (composition)
**/

const identityʹ = t => eq => x => {
const a = t(x)[promap](identity, identity);
const b = t(x);
return eq(a, b);
};

const composition = t => eq => x => {
const a = t(x)[promap](compose(identity)(identity), compose(identity)(identity));
const b = t(x)[promap](identity, identity)[promap](identity, identity);
return eq(a, b);
};

module.exports = { identity: identityʹ
, composition
};

0 comments on commit 30638f5

Please sign in to comment.