Repository: gcanti/fp-ts
Pull request #959 (2019-10-01 07:39:43)

add constrained Filterable instance to IOEither, TaskEither
A few doubts:
- ~I'm not sure the tests add any value (if yes, I will add them for other functions too)~ EDIT: left a single one just to obtain the necessary jest coverage
- ~I wanted to add `getFilterable` to `Either` but then stopped and just reused `getWitherable` in order to avoid duplicating part of the logic. If you feel it would add clarity, even at the cost of some duplication, I will split them~ EDIT: discussed offline with @gcanti , it should be already aligned to other similar constrained instances, where a new function has been added only if the constraint changes



Patch:
diff --git a/docs/modules/IOEither.ts.md b/docs/modules/IOEither.ts.md
index 68909711..c263e960 100644
--- a/docs/modules/IOEither.ts.md
+++ b/docs/modules/IOEither.ts.md
@@ -26,6 +26,7 @@ error of type `E`. If you want to represent a synchronous computation that never
 - [fold (function)](#fold-function)
 - [getApplyMonoid (function)](#getapplymonoid-function)
 - [getApplySemigroup (function)](#getapplysemigroup-function)
+- [getFilterable (function)](#getfilterable-function)
 - [getIOValidation (function)](#getiovalidation-function)
 - [getOrElse (function)](#getorelse-function)
 - [getSemigroup (function)](#getsemigroup-function)
@@ -186,6 +187,16 @@ export function getApplySemigroup<E, A>(S: Semigroup<A>): Semigroup<IOEither<E,
 
 Added in v2.0.0
 
+# getFilterable (function)
+
+**Signature**
+
+```ts
+export function getFilterable<E>(M: Monoid<E>): Filterable2C<URI, E> { ... }
+```
+
+Added in v2.1.0
+
 # getIOValidation (function)
 
 **Signature**
diff --git a/docs/modules/TaskEither.ts.md b/docs/modules/TaskEither.ts.md
index 2fbe5015..e4036854 100644
--- a/docs/modules/TaskEither.ts.md
+++ b/docs/modules/TaskEither.ts.md
@@ -28,6 +28,7 @@ error of type `E`. If you want to represent an asynchronous computation that nev
 - [fold (function)](#fold-function)
 - [getApplyMonoid (function)](#getapplymonoid-function)
 - [getApplySemigroup (function)](#getapplysemigroup-function)
+- [getFilterable (function)](#getfilterable-function)
 - [getOrElse (function)](#getorelse-function)
 - [getSemigroup (function)](#getsemigroup-function)
 - [getTaskValidation (function)](#gettaskvalidation-function)
@@ -216,6 +217,16 @@ export function getApplySemigroup<E, A>(S: Semigroup<A>): Semigroup<TaskEither<E
 
 Added in v2.0.0
 
+# getFilterable (function)
+
+**Signature**
+
+```ts
+export function getFilterable<E>(M: Monoid<E>): Filterable2C<URI, E> { ... }
+```
+
+Added in v2.1.0
+
 # getOrElse (function)
 
 **Signature**
diff --git a/docs/recipes/equality.md b/docs/recipes/equality.md
index f7a1df43..490b0e95 100644
--- a/docs/recipes/equality.md
+++ b/docs/recipes/equality.md
@@ -88,9 +88,9 @@ const eqPoint: Eq<Point> = getStructEq({
   y: eqNumber
 })
 
-const setoidArrayOfPoints: Eq<Array<Point>> = getEq(eqPoint)
+const eqArrayOfPoints: Eq<Array<Point>> = getEq(eqPoint)
 
-setoidArrayOfPoints.equals(
+eqArrayOfPoints.equals(
   [{ x: 0, y: 0 }, { x: 4, y: 0 }],
   [{ x: 0, y: 0 }, { x: 4, y: 0 }]
 ) // true
@@ -116,13 +116,13 @@ eqUserId.equals({ userId: 1, name: 'Giulio' }, { userId: 2, name: 'Giulio' }) //
 
 ## More `Eq` instances
 
-Many data types provide `Setoid` instances. Here's [Option](../modules/Option.ts):
+Many data types provide `Eq` instances. Here's [Option](../modules/Option.ts):
 
 ```ts
 import { getEq, none, some } from 'fp-ts/lib/Option'
-import { setoidNumber } from 'fp-ts/lib/Setoid'
+import { eqNumber } from 'fp-ts/lib/Eq'
 
-const E = getEq(setoidNumber)
+const E = getEq(eqNumber)
 
 E.equals(some(3), some(3)) // true
 E.equals(none, some(4)) // false
diff --git a/docs/type-classes.dot b/docs/type-classes.dot
index e38476c9..12c2dbab 100644
--- a/docs/type-classes.dot
+++ b/docs/type-classes.dot
@@ -29,12 +29,12 @@ digraph G {
 
   node [style=filled,color=black,fillcolor="#FC81BF"];
   "Ord" [URL="./modules/Ord.ts.html"];
-  "Setoid" [URL="./modules/Setoid.ts.html"];
+  "Eq" [URL="./modules/Eq.ts.html"];
 
   node [style=filled,color=black,fillcolor="#FECFE6"];
   "Bounded" [URL="./modules/Bounded.ts.html"];
 
-  "Bounded" -> "Ord" -> "Setoid"
+  "Bounded" -> "Ord" -> "Eq"
 
   node [style=filled,color=black,fillcolor="#FDF4AA"];
   "Category" [URL="./modules/Category.ts.html"];
diff --git a/docs/type-classes.svg b/docs/type-classes.svg
index d7767a5a..f7ff3244 100644
--- a/docs/type-classes.svg
+++ b/docs/type-classes.svg
@@ -158,18 +158,18 @@
 </a>
 </g>
 </g>
-<!-- Setoid -->
+<!-- Eq -->
 <g id="node12" class="node">
-<title>Setoid</title>
-<g id="a_node12"><a xlink:href="./modules/Setoid.ts.html" xlink:title="Setoid">
+<title>Eq</title>
+<g id="a_node12"><a xlink:href="./modules/Eq.ts.html" xlink:title="Eq">
 <ellipse fill="#fc81bf" stroke="#000000" cx="1134.0068" cy="-162" rx="38.3165" ry="18"></ellipse>
-<text text-anchor="middle" x="1134.0068" y="-157.8" font-family="arial" font-size="14.00" fill="#000000">Setoid</text>
+<text text-anchor="middle" x="1134.0068" y="-157.8" font-family="arial" font-size="14.00" fill="#000000">Eq</text>
 </a>
 </g>
 </g>
-<!-- Ord&#45;&gt;Setoid -->
+<!-- Ord&#45;&gt;Eq -->
 <g id="edge9" class="edge">
-<title>Ord-&gt;Setoid</title>
+<title>Ord-&gt;Eq</title>
 <path fill="none" stroke="#000000" d="M1134.0068,-108.1686C1134.0068,-115.869 1134.0068,-125.0257 1134.0068,-133.5834"></path>
 <polygon fill="#000000" stroke="#000000" points="1130.5069,-133.5867 1134.0068,-143.5867 1137.5069,-133.5868 1130.5069,-133.5867"></polygon>
 </g>
diff --git a/src/IOEither.ts b/src/IOEither.ts
index 12c493cc..a50e4004 100644
--- a/src/IOEither.ts
+++ b/src/IOEither.ts
@@ -15,6 +15,7 @@ import { Monoid } from './Monoid'
 import { pipeable } from './pipeable'
 import { Semigroup } from './Semigroup'
 import { getValidationM } from './ValidationT'
+import { Filterable2C, getFilterableComposition } from './Filterable'
 
 import Either = E.Either
 
@@ -149,6 +150,21 @@ export function getIOValidation<E>(S: Semigroup<E>): Monad2C<URI, E> & Alt2C<URI
   }
 }
 
+const phantom: any = undefined
+
+/**
+ * @since 2.1.0
+ */
+export function getFilterable<E>(M: Monoid<E>): Filterable2C<URI, E> {
+  const F = E.getWitherable(M)
+
+  return {
+    URI,
+    _E: phantom,
+    ...getFilterableComposition(io, F)
+  }
+}
+
 /**
  * @since 2.0.0
  */
diff --git a/src/TaskEither.ts b/src/TaskEither.ts
index 84b84352..e94debf1 100644
--- a/src/TaskEither.ts
+++ b/src/TaskEither.ts
@@ -17,6 +17,7 @@ import { pipeable } from './pipeable'
 import { Semigroup } from './Semigroup'
 import { getSemigroup as getTaskSemigroup, Task, task } from './Task'
 import { getValidationM } from './ValidationT'
+import { Filterable2C, getFilterableComposition } from './Filterable'
 
 import Either = E.Either
 
@@ -228,6 +229,21 @@ export function getTaskValidation<E>(S: Semigroup<E>): Monad2C<URI, E> & Alt2C<U
   }
 }
 
+const phantom: any = undefined
+
+/**
+ * @since 2.1.0
+ */
+export function getFilterable<E>(M: Monoid<E>): Filterable2C<URI, E> {
+  const F = E.getWitherable(M)
+
+  return {
+    URI,
+    _E: phantom,
+    ...getFilterableComposition(task, F)
+  }
+}
+
 /**
  * @since 2.0.0
  */


Test patch:
diff --git a/test/IOEither.ts b/test/IOEither.ts
index 55af3a93..5dab9e3e 100644
--- a/test/IOEither.ts
+++ b/test/IOEither.ts
@@ -5,7 +5,8 @@ import * as _ from '../src/IOEither'
 import { monoidString } from '../src/Monoid'
 import { semigroupSum, semigroupString } from '../src/Semigroup'
 import { none, some } from '../src/Option'
-import { pipe } from '../src/pipeable'
+import { pipe, pipeable } from '../src/pipeable'
+import { getMonoid } from '../src/Array'
 
 describe('IOEither', () => {
   it('fold', () => {
@@ -256,4 +257,30 @@ describe('IOEither', () => {
       assert.deepStrictEqual(e4, E.left('ab'))
     })
   })
+
+  describe('getFilterable', () => {
+    const F_ = {
+      ..._.ioEither,
+      ..._.getFilterable(getMonoid<string>())
+    }
+    const { filter } = pipeable(F_)
+
+    it('filter', async () => {
+      const r1 = pipe(
+        _.right(1),
+        filter(n => n > 0)
+      )
+      assert.deepStrictEqual(r1(), _.right(1)())
+      const r2 = pipe(
+        _.right(-1),
+        filter(n => n > 0)
+      )
+      assert.deepStrictEqual(r2(), _.left([])())
+      const r3 = pipe(
+        _.left(['a']),
+        filter(n => n > 0)
+      )
+      assert.deepStrictEqual(r3(), _.left(['a'])())
+    })
+  })
 })
diff --git a/test/TaskEither.ts b/test/TaskEither.ts
index 02232e24..a054a818 100644
--- a/test/TaskEither.ts
+++ b/test/TaskEither.ts
@@ -1,10 +1,10 @@
 import * as assert from 'assert'
-import { array } from '../src/Array'
+import { array, getMonoid } from '../src/Array'
 import * as E from '../src/Either'
 import { io } from '../src/IO'
 import { monoidString } from '../src/Monoid'
 import { none, some } from '../src/Option'
-import { pipe } from '../src/pipeable'
+import { pipe, pipeable } from '../src/pipeable'
 import { semigroupString, semigroupSum } from '../src/Semigroup'
 import * as _ from '../src/TaskEither'
 
@@ -329,4 +329,30 @@ describe('TaskEither', () => {
       assert.deepStrictEqual(e4, E.left('ab'))
     })
   })
+
+  describe('getFilterable', () => {
+    const F_ = {
+      ..._.taskEither,
+      ..._.getFilterable(getMonoid<string>())
+    }
+    const { filter } = pipeable(F_)
+
+    it('filter', async () => {
+      const r1 = pipe(
+        _.right(1),
+        filter(n => n > 0)
+      )
+      assert.deepStrictEqual(await r1(), await _.right(1)())
+      const r2 = pipe(
+        _.right(-1),
+        filter(n => n > 0)
+      )
+      assert.deepStrictEqual(await r2(), await _.left([])())
+      const r3 = pipe(
+        _.left(['a']),
+        filter(n => n > 0)
+      )
+      assert.deepStrictEqual(await r3(), await _.left(['a'])())
+    })
+  })
 })