Skip to content

Commit

Permalink
add Filterable algebra
Browse files Browse the repository at this point in the history
  • Loading branch information
davidchambers committed Oct 14, 2017
1 parent 6b4f2d7 commit 617e26c
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 3 deletions.
44 changes: 42 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ structures:
* [Applicative](#applicative)
* [Alt](#alt)
* [Plus](#plus)
* [Filterable](#filterable)
* [Alternative](#alternative)
* [Foldable](#foldable)
* [Traversable](#traversable)
Expand All @@ -33,7 +34,7 @@ structures:
* [Bifunctor](#bifunctor)
* [Profunctor](#profunctor)

<img src="figures/dependencies.png" width="888" height="257" />
<img src="figures/dependencies.png" width="888" height="245" />

## General

Expand Down Expand Up @@ -497,6 +498,34 @@ Given a value `x`, one can access its type representative via the

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

### Filterable

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

1. `f.filter(x => true)` is equivalent to `f` (identity)
2. `f.filter(x => false)` is equivalent to `f.constructor.zero()` (annihilation)
3. `f.filter(p).filter(p)` is equivalent to `f.filter(p)` (idempotence)

#### `filter` method

```hs
filter :: Filterable f => f a ~> (a -> Boolean) -> f a
```

A value which has a Filterable must provide a `filter` method. The `filter`
method takes one argument:

f.filter(p)

1. `p` must be a function.

1. If `p` is not a function, the behaviour of `filter` is unspecified.
2. `p` must return either `true` or `false`. If it returns any other value,
the behaviour of `filter` is unspecified.

2. `filter` must return a value of the same Filterable.

### Alternative

A value that implements the Alternative specification must also implement
Expand Down Expand Up @@ -836,7 +865,7 @@ to implement certain methods then derive the remaining methods. Derivations:
function(f) {
function Id(value) {
this.value = value;
};
}
Id.of = function(x) {
return new Id(x);
};
Expand All @@ -850,6 +879,15 @@ to implement certain methods then derive the remaining methods. Derivations:
}
```

- [`filter`][] may be derived from [`of`][], [`chain`][], and [`zero`][]:

```js
function(pred) {
var F = this.constructor;
return this.chain(x => pred(x) ? F.of(x) : F.zero());
}
```

If a data type provides a method which *could* be derived, its behaviour must
be equivalent to that of the derivation (or derivations).

Expand All @@ -873,12 +911,14 @@ be equivalent to that of the derivation (or derivations).
[`equals`]: #equals-method
[`extend`]: #extend-method
[`extract`]: #extract-method
[`filter`]: #filter-method
[`lte`]: #lte-method
[`map`]: #map-method
[`of`]: #of-method
[`promap`]: #promap-method
[`reduce`]: #reduce-method
[`sequence`]: #sequence-method
[`zero`]: #zero-method

## Alternatives

Expand Down
4 changes: 3 additions & 1 deletion figures/dependencies.dot
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@ digraph {
Chain;
ChainRec;
Comonad;
Contravariant;
Extend;
Filterable;
Foldable;
Functor;
Group;
Contravariant;
Monad;
Monoid;
Ord;
Expand Down Expand Up @@ -44,6 +45,7 @@ digraph {
Functor -> Traversable;
Monoid -> Group;
Plus -> Alternative;
Plus -> Filterable;
Semigroup -> Monoid;
Semigroupoid -> Category;
Setoid -> Ord;
Expand Down
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.
1 change: 1 addition & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
of: 'fantasy-land/of',
alt: 'fantasy-land/alt',
zero: 'fantasy-land/zero',
filter: 'fantasy-land/filter',
reduce: 'fantasy-land/reduce',
traverse: 'fantasy-land/traverse',
chain: 'fantasy-land/chain',
Expand Down
33 changes: 33 additions & 0 deletions laws/filterable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
'use strict';

const {filter, zero} = require('..');

/**
### Filterable
1. `f.filter(x => true)` is equivalent to `f` (identity)
2. `f.filter(x => false)` is equivalent to `f.constructor.zero()` (annihilation)
3. `f.filter(p).filter(p)` is equivalent to `f.filter(p)` (idempotence)
**/

const identity = eq => filterable => {
const a = filterable[filter](x => true);
const b = filterable;
return eq(a, b);
};

const annihilation = eq => filterable => {
const a = filterable[filter](x => false);
const b = filterable.constructor[zero]();
return eq(a, b);
};

const idempotence = eq => pred => filterable => {
const a = filterable[filter](pred)[filter](pred);
const b = filterable[filter](pred);
return eq(a, b);
};

module.exports = {identity, annihilation, idempotence};

0 comments on commit 617e26c

Please sign in to comment.