Skip to content

Commit 617e26c

Browse files
committed
add Filterable algebra
1 parent 6b4f2d7 commit 617e26c

File tree

5 files changed

+79
-3
lines changed

5 files changed

+79
-3
lines changed

README.md

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ structures:
2222
* [Applicative](#applicative)
2323
* [Alt](#alt)
2424
* [Plus](#plus)
25+
* [Filterable](#filterable)
2526
* [Alternative](#alternative)
2627
* [Foldable](#foldable)
2728
* [Traversable](#traversable)
@@ -33,7 +34,7 @@ structures:
3334
* [Bifunctor](#bifunctor)
3435
* [Profunctor](#profunctor)
3536

36-
<img src="figures/dependencies.png" width="888" height="257" />
37+
<img src="figures/dependencies.png" width="888" height="245" />
3738

3839
## General
3940

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

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

501+
### Filterable
502+
503+
A value that implements the Filterable specification must also implement
504+
the [Plus](#plus) specification.
505+
506+
1. `f.filter(x => true)` is equivalent to `f` (identity)
507+
2. `f.filter(x => false)` is equivalent to `f.constructor.zero()` (annihilation)
508+
3. `f.filter(p).filter(p)` is equivalent to `f.filter(p)` (idempotence)
509+
510+
#### `filter` method
511+
512+
```hs
513+
filter :: Filterable f => f a ~> (a -> Boolean) -> f a
514+
```
515+
516+
A value which has a Filterable must provide a `filter` method. The `filter`
517+
method takes one argument:
518+
519+
f.filter(p)
520+
521+
1. `p` must be a function.
522+
523+
1. If `p` is not a function, the behaviour of `filter` is unspecified.
524+
2. `p` must return either `true` or `false`. If it returns any other value,
525+
the behaviour of `filter` is unspecified.
526+
527+
2. `filter` must return a value of the same Filterable.
528+
500529
### Alternative
501530

502531
A value that implements the Alternative specification must also implement
@@ -836,7 +865,7 @@ to implement certain methods then derive the remaining methods. Derivations:
836865
function(f) {
837866
function Id(value) {
838867
this.value = value;
839-
};
868+
}
840869
Id.of = function(x) {
841870
return new Id(x);
842871
};
@@ -850,6 +879,15 @@ to implement certain methods then derive the remaining methods. Derivations:
850879
}
851880
```
852881

882+
- [`filter`][] may be derived from [`of`][], [`chain`][], and [`zero`][]:
883+
884+
```js
885+
function(pred) {
886+
var F = this.constructor;
887+
return this.chain(x => pred(x) ? F.of(x) : F.zero());
888+
}
889+
```
890+
853891
If a data type provides a method which *could* be derived, its behaviour must
854892
be equivalent to that of the derivation (or derivations).
855893

@@ -873,12 +911,14 @@ be equivalent to that of the derivation (or derivations).
873911
[`equals`]: #equals-method
874912
[`extend`]: #extend-method
875913
[`extract`]: #extract-method
914+
[`filter`]: #filter-method
876915
[`lte`]: #lte-method
877916
[`map`]: #map-method
878917
[`of`]: #of-method
879918
[`promap`]: #promap-method
880919
[`reduce`]: #reduce-method
881920
[`sequence`]: #sequence-method
921+
[`zero`]: #zero-method
882922

883923
## Alternatives
884924

figures/dependencies.dot

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,12 @@ digraph {
1111
Chain;
1212
ChainRec;
1313
Comonad;
14+
Contravariant;
1415
Extend;
16+
Filterable;
1517
Foldable;
1618
Functor;
1719
Group;
18-
Contravariant;
1920
Monad;
2021
Monoid;
2122
Ord;
@@ -44,6 +45,7 @@ digraph {
4445
Functor -> Traversable;
4546
Monoid -> Group;
4647
Plus -> Alternative;
48+
Plus -> Filterable;
4749
Semigroup -> Monoid;
4850
Semigroupoid -> Category;
4951
Setoid -> Ord;

figures/dependencies.png

3.57 KB
Loading

index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
of: 'fantasy-land/of',
2020
alt: 'fantasy-land/alt',
2121
zero: 'fantasy-land/zero',
22+
filter: 'fantasy-land/filter',
2223
reduce: 'fantasy-land/reduce',
2324
traverse: 'fantasy-land/traverse',
2425
chain: 'fantasy-land/chain',

laws/filterable.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
'use strict';
2+
3+
const {filter, zero} = require('..');
4+
5+
/**
6+
7+
### Filterable
8+
9+
1. `f.filter(x => true)` is equivalent to `f` (identity)
10+
2. `f.filter(x => false)` is equivalent to `f.constructor.zero()` (annihilation)
11+
3. `f.filter(p).filter(p)` is equivalent to `f.filter(p)` (idempotence)
12+
13+
**/
14+
15+
const identity = eq => filterable => {
16+
const a = filterable[filter](x => true);
17+
const b = filterable;
18+
return eq(a, b);
19+
};
20+
21+
const annihilation = eq => filterable => {
22+
const a = filterable[filter](x => false);
23+
const b = filterable.constructor[zero]();
24+
return eq(a, b);
25+
};
26+
27+
const idempotence = eq => pred => filterable => {
28+
const a = filterable[filter](pred)[filter](pred);
29+
const b = filterable[filter](pred);
30+
return eq(a, b);
31+
};
32+
33+
module.exports = {identity, annihilation, idempotence};

0 commit comments

Comments
 (0)