diff --git a/README.md b/README.md
index 9a7b19b..a16bfff 100644
--- a/README.md
+++ b/README.md
@@ -22,6 +22,7 @@ structures:
* [Applicative](#applicative)
* [Alt](#alt)
* [Plus](#plus)
+* [Filterable](#filterable)
* [Alternative](#alternative)
* [Foldable](#foldable)
* [Traversable](#traversable)
@@ -33,7 +34,7 @@ structures:
* [Bifunctor](#bifunctor)
* [Profunctor](#profunctor)
-
+
## General
@@ -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
@@ -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);
};
@@ -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).
@@ -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
diff --git a/figures/dependencies.dot b/figures/dependencies.dot
index d632f3f..3d102ea 100644
--- a/figures/dependencies.dot
+++ b/figures/dependencies.dot
@@ -11,11 +11,12 @@ digraph {
Chain;
ChainRec;
Comonad;
+ Contravariant;
Extend;
+ Filterable;
Foldable;
Functor;
Group;
- Contravariant;
Monad;
Monoid;
Ord;
@@ -44,6 +45,7 @@ digraph {
Functor -> Traversable;
Monoid -> Group;
Plus -> Alternative;
+ Plus -> Filterable;
Semigroup -> Monoid;
Semigroupoid -> Category;
Setoid -> Ord;
diff --git a/figures/dependencies.png b/figures/dependencies.png
index e4d814b..a94963c 100644
Binary files a/figures/dependencies.png and b/figures/dependencies.png differ
diff --git a/index.js b/index.js
index 6e0e13e..a3cefd0 100644
--- a/index.js
+++ b/index.js
@@ -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',
diff --git a/laws/filterable.js b/laws/filterable.js
new file mode 100644
index 0000000..6f207a6
--- /dev/null
+++ b/laws/filterable.js
@@ -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};