Repository: gcanti/fp-ts
Pull request #1058 (2019-12-20 14:19:15)

add some kleisli chains
RE: https://github.com/gcanti/fp-ts/issues/1049#issuecomment-566923339


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4f3f79e4..26cf0caf 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -22,6 +22,21 @@ high state of flux, you're at risk of it changing without notice.
     - add `getMonoid` (@vicrac)
   - `Ordering`
     - add `monoidOrdering` (@gcanti)
+  - `Task`
+    - add `chainIO` (@gcanti)
+  - `TaskEither`
+    - add `chainEither`, `chainIOEither` (@gcanti)
+  - `IOEither`
+    - add `chainEither` (@gcanti)
+  - `ReaderEither`
+    - add `chainEither` (@gcanti)
+  - `ReaderTask`
+    - add `run` (@gcanti)
+    - add `chainIO`, `chainTask` (@gcanti)
+  - `ReaderTaskEither`
+    - add `chainEither`, `chainIOEither`, `chainTaskEither` (@gcanti)
+  - `StateReaderTaskEither`
+    - add `chainEither`, `chainIOEither`, `chainTaskEither`, `chainReaderTaskEither` (@gcanti)
 - **Deprecation**
   - `Ord`
     - deprecate `getSemigroup` in favor of `getMonoid` (@gcanti)
diff --git a/docs/modules/IOEither.ts.md b/docs/modules/IOEither.ts.md
index 88d09a77..1aefc812 100644
--- a/docs/modules/IOEither.ts.md
+++ b/docs/modules/IOEither.ts.md
@@ -25,6 +25,7 @@ Added in v2.0.0
 - [rightIO (constant)](#rightio-constant)
 - [swap (constant)](#swap-constant)
 - [bracket (function)](#bracket-function)
+- [chainEither (function)](#chaineither-function)
 - [fold (function)](#fold-function)
 - [getApplyMonoid (function)](#getapplymonoid-function)
 - [getApplySemigroup (function)](#getapplysemigroup-function)
@@ -160,6 +161,16 @@ export function bracket<E, A, B>(
 
 Added in v2.0.0
 
+# chainEither (function)
+
+**Signature**
+
+```ts
+export function chainEither<E, A, B>(f: (a: A) => Either<E, B>): (ma: IOEither<E, A>) => IOEither<E, B> { ... }
+```
+
+Added in v2.4.0
+
 # fold (function)
 
 **Signature**
diff --git a/docs/modules/ReaderEither.ts.md b/docs/modules/ReaderEither.ts.md
index ac53dd72..e9fa2b69 100644
--- a/docs/modules/ReaderEither.ts.md
+++ b/docs/modules/ReaderEither.ts.md
@@ -23,6 +23,7 @@ Added in v2.0.0
 - [swap (constant)](#swap-constant)
 - [ask (function)](#ask-function)
 - [asks (function)](#asks-function)
+- [chainEither (function)](#chaineither-function)
 - [fold (function)](#fold-function)
 - [getApplyMonoid (function)](#getapplymonoid-function)
 - [getApplySemigroup (function)](#getapplysemigroup-function)
@@ -158,6 +159,18 @@ export function asks<R, E = never, A = never>(f: (r: R) => A): ReaderEither<R, E
 
 Added in v2.0.0
 
+# chainEither (function)
+
+**Signature**
+
+```ts
+export function chainEither<E, A, B>(
+  f: (a: A) => Either<E, B>
+): <R>(ma: ReaderEither<R, E, A>) => ReaderEither<R, E, B> { ... }
+```
+
+Added in v2.4.0
+
 # fold (function)
 
 **Signature**
diff --git a/docs/modules/ReaderTask.ts.md b/docs/modules/ReaderTask.ts.md
index 3cf8e410..62fb1d5b 100644
--- a/docs/modules/ReaderTask.ts.md
+++ b/docs/modules/ReaderTask.ts.md
@@ -22,10 +22,13 @@ Added in v2.3.0
 - [of (constant)](#of-constant)
 - [readerTask (constant)](#readertask-constant)
 - [readerTaskSeq (constant)](#readertaskseq-constant)
+- [chainIO (function)](#chainio-function)
+- [chainTask (function)](#chaintask-function)
 - [fromIO (function)](#fromio-function)
 - [getMonoid (function)](#getmonoid-function)
 - [getSemigroup (function)](#getsemigroup-function)
 - [local (function)](#local-function)
+- [run (function)](#run-function)
 - [ap (export)](#ap-export)
 - [apFirst (export)](#apfirst-export)
 - [apSecond (export)](#apsecond-export)
@@ -140,6 +143,26 @@ export const readerTaskSeq: typeof readerTask = ...
 
 Added in v2.3.0
 
+# chainIO (function)
+
+**Signature**
+
+```ts
+export function chainIO<A, B>(f: (a: A) => IO<B>): <R>(ma: ReaderTask<R, A>) => ReaderTask<R, B> { ... }
+```
+
+Added in v2.4.0
+
+# chainTask (function)
+
+**Signature**
+
+```ts
+export function chainTask<A, B>(f: (a: A) => Task<B>): <R>(ma: ReaderTask<R, A>) => ReaderTask<R, B> { ... }
+```
+
+Added in v2.4.0
+
 # fromIO (function)
 
 **Signature**
@@ -180,6 +203,16 @@ export function local<Q, R>(f: (f: Q) => R): <E, A>(ma: ReaderTask<R, A>) => Rea
 
 Added in v2.3.0
 
+# run (function)
+
+**Signature**
+
+```ts
+export function run<R, A>(ma: ReaderTask<R, A>, r: R): Promise<A> { ... }
+```
+
+Added in v2.4.0
+
 # ap (export)
 
 **Signature**
diff --git a/docs/modules/ReaderTaskEither.ts.md b/docs/modules/ReaderTaskEither.ts.md
index b0a9543d..55ab8b08 100644
--- a/docs/modules/ReaderTaskEither.ts.md
+++ b/docs/modules/ReaderTaskEither.ts.md
@@ -23,6 +23,9 @@ Added in v2.0.0
 - [right (constant)](#right-constant)
 - [rightReader (constant)](#rightreader-constant)
 - [bracket (function)](#bracket-function)
+- [chainEither (function)](#chaineither-function)
+- [chainIOEither (function)](#chainioeither-function)
+- [chainTaskEither (function)](#chaintaskeither-function)
 - [fold (function)](#fold-function)
 - [fromIOEither (function)](#fromioeither-function)
 - [fromReaderEither (function)](#fromreadereither-function)
@@ -181,6 +184,42 @@ export function bracket<R, E, A, B>(
 
 Added in v2.0.4
 
+# chainEither (function)
+
+**Signature**
+
+```ts
+export function chainEither<E, A, B>(
+  f: (a: A) => Either<E, B>
+): <R>(ma: ReaderTaskEither<R, E, A>) => ReaderTaskEither<R, E, B> { ... }
+```
+
+Added in v2.4.0
+
+# chainIOEither (function)
+
+**Signature**
+
+```ts
+export function chainIOEither<E, A, B>(
+  f: (a: A) => IOEither<E, B>
+): <R>(ma: ReaderTaskEither<R, E, A>) => ReaderTaskEither<R, E, B> { ... }
+```
+
+Added in v2.4.0
+
+# chainTaskEither (function)
+
+**Signature**
+
+```ts
+export function chainTaskEither<E, A, B>(
+  f: (a: A) => TaskEither<E, B>
+): <R>(ma: ReaderTaskEither<R, E, A>) => ReaderTaskEither<R, E, B> { ... }
+```
+
+Added in v2.4.0
+
 # fold (function)
 
 **Signature**
diff --git a/docs/modules/StateReaderTaskEither.ts.md b/docs/modules/StateReaderTaskEither.ts.md
index e817f166..8086fa69 100644
--- a/docs/modules/StateReaderTaskEither.ts.md
+++ b/docs/modules/StateReaderTaskEither.ts.md
@@ -26,6 +26,10 @@ Added in v2.0.0
 - [rightState (constant)](#rightstate-constant)
 - [stateReaderTaskEither (constant)](#statereadertaskeither-constant)
 - [stateReaderTaskEitherSeq (constant)](#statereadertaskeitherseq-constant)
+- [chainEither (function)](#chaineither-function)
+- [chainIOEither (function)](#chainioeither-function)
+- [chainReaderTaskEither (function)](#chainreadertaskeither-function)
+- [chainTaskEither (function)](#chaintaskeither-function)
 - [fromIOEither (function)](#fromioeither-function)
 - [fromReaderEither (function)](#fromreadereither-function)
 - [fromTaskEither (function)](#fromtaskeither-function)
@@ -206,6 +210,54 @@ export const stateReaderTaskEitherSeq: typeof stateReaderTaskEither = ...
 
 Added in v2.0.0
 
+# chainEither (function)
+
+**Signature**
+
+```ts
+export function chainEither<E, A, B>(
+  f: (a: A) => Either<E, B>
+): <S, R>(ma: StateReaderTaskEither<S, R, E, A>) => StateReaderTaskEither<S, R, E, B> { ... }
+```
+
+Added in v2.4.0
+
+# chainIOEither (function)
+
+**Signature**
+
+```ts
+export function chainIOEither<E, A, B>(
+  f: (a: A) => IOEither<E, B>
+): <S, R>(ma: StateReaderTaskEither<S, R, E, A>) => StateReaderTaskEither<S, R, E, B> { ... }
+```
+
+Added in v2.4.0
+
+# chainReaderTaskEither (function)
+
+**Signature**
+
+```ts
+export function chainReaderTaskEither<R, E, A, B>(
+  f: (a: A) => ReaderTaskEither<R, E, B>
+): <S>(ma: StateReaderTaskEither<S, R, E, A>) => StateReaderTaskEither<S, R, E, B> { ... }
+```
+
+Added in v2.4.0
+
+# chainTaskEither (function)
+
+**Signature**
+
+```ts
+export function chainTaskEither<E, A, B>(
+  f: (a: A) => TaskEither<E, B>
+): <S, R>(ma: StateReaderTaskEither<S, R, E, A>) => StateReaderTaskEither<S, R, E, B> { ... }
+```
+
+Added in v2.4.0
+
 # fromIOEither (function)
 
 **Signature**
diff --git a/docs/modules/Task.ts.md b/docs/modules/Task.ts.md
index 8e71566c..a0b06835 100644
--- a/docs/modules/Task.ts.md
+++ b/docs/modules/Task.ts.md
@@ -20,6 +20,7 @@ Added in v2.0.0
 - [URI (constant)](#uri-constant)
 - [task (constant)](#task-constant)
 - [taskSeq (constant)](#taskseq-constant)
+- [chainIO (function)](#chainio-function)
 - [delay (function)](#delay-function)
 - [fromIO (function)](#fromio-function)
 - [getMonoid (function)](#getmonoid-function)
@@ -91,6 +92,16 @@ export const taskSeq: typeof task = ...
 
 Added in v2.0.0
 
+# chainIO (function)
+
+**Signature**
+
+```ts
+export function chainIO<A, B>(f: (a: A) => IO<B>): (ma: Task<A>) => Task<B> { ... }
+```
+
+Added in v2.4.0
+
 # delay (function)
 
 **Signature**
diff --git a/docs/modules/TaskEither.ts.md b/docs/modules/TaskEither.ts.md
index 4bdc12ba..bfdd9b56 100644
--- a/docs/modules/TaskEither.ts.md
+++ b/docs/modules/TaskEither.ts.md
@@ -27,6 +27,8 @@ Added in v2.0.0
 - [taskEither (constant)](#taskeither-constant)
 - [taskEitherSeq (constant)](#taskeitherseq-constant)
 - [bracket (function)](#bracket-function)
+- [chainEither (function)](#chaineither-function)
+- [chainIOEither (function)](#chainioeither-function)
 - [fold (function)](#fold-function)
 - [getApplyMonoid (function)](#getapplymonoid-function)
 - [getApplySemigroup (function)](#getapplysemigroup-function)
@@ -187,6 +189,26 @@ export function bracket<E, A, B>(
 
 Added in v2.0.0
 
+# chainEither (function)
+
+**Signature**
+
+```ts
+export function chainEither<E, A, B>(f: (a: A) => Either<E, B>): (ma: TaskEither<E, A>) => TaskEither<E, B> { ... }
+```
+
+Added in v2.4.0
+
+# chainIOEither (function)
+
+**Signature**
+
+```ts
+export function chainIOEither<E, A, B>(f: (a: A) => IOEither<E, B>): (ma: TaskEither<E, A>) => TaskEither<E, B> { ... }
+```
+
+Added in v2.4.0
+
 # fold (function)
 
 **Signature**
diff --git a/src/IOEither.ts b/src/IOEither.ts
index e5f378ef..b9f3e358 100644
--- a/src/IOEither.ts
+++ b/src/IOEither.ts
@@ -172,6 +172,13 @@ export function getFilterable<E>(M: Monoid<E>): Filterable2C<URI, E> {
   }
 }
 
+/**
+ * @since 2.4.0
+ */
+export function chainEither<E, A, B>(f: (a: A) => Either<E, B>): (ma: IOEither<E, A>) => IOEither<E, B> {
+  return chain(a => fromEither(f(a)))
+}
+
 /**
  * @since 2.0.0
  */
diff --git a/src/ReaderEither.ts b/src/ReaderEither.ts
index a9a95d91..e8a55e40 100644
--- a/src/ReaderEither.ts
+++ b/src/ReaderEither.ts
@@ -151,6 +151,15 @@ export function getReaderValidation<E>(
   }
 }
 
+/**
+ * @since 2.4.0
+ */
+export function chainEither<E, A, B>(
+  f: (a: A) => Either<E, B>
+): <R>(ma: ReaderEither<R, E, A>) => ReaderEither<R, E, B> {
+  return chain(a => fromEither(f(a)))
+}
+
 /**
  * @since 2.0.0
  */
diff --git a/src/ReaderTask.ts b/src/ReaderTask.ts
index 9eb56620..0e8f9057 100644
--- a/src/ReaderTask.ts
+++ b/src/ReaderTask.ts
@@ -38,6 +38,13 @@ export interface ReaderTask<R, A> {
   (r: R): Task<A>
 }
 
+/**
+ * @since 2.4.0
+ */
+export function run<R, A>(ma: ReaderTask<R, A>, r: R): Promise<A> {
+  return ma(r)()
+}
+
 /**
  * @since 2.3.0
  */
@@ -94,6 +101,20 @@ export function local<Q, R>(f: (f: Q) => R): <E, A>(ma: ReaderTask<R, A>) => Rea
   return ma => T.local(ma, f)
 }
 
+/**
+ * @since 2.4.0
+ */
+export function chainIO<A, B>(f: (a: A) => IO<B>): <R>(ma: ReaderTask<R, A>) => ReaderTask<R, B> {
+  return chain(a => fromIO(f(a)))
+}
+
+/**
+ * @since 2.4.0
+ */
+export function chainTask<A, B>(f: (a: A) => Task<B>): <R>(ma: ReaderTask<R, A>) => ReaderTask<R, B> {
+  return chain(a => fromTask(f(a)))
+}
+
 /**
  * @since 2.3.0
  */
diff --git a/src/ReaderTaskEither.ts b/src/ReaderTaskEither.ts
index 7aaa472e..35e8aad4 100644
--- a/src/ReaderTaskEither.ts
+++ b/src/ReaderTaskEither.ts
@@ -248,6 +248,33 @@ export function getReaderTaskValidation<E>(
   }
 }
 
+/**
+ * @since 2.4.0
+ */
+export function chainEither<E, A, B>(
+  f: (a: A) => Either<E, B>
+): <R>(ma: ReaderTaskEither<R, E, A>) => ReaderTaskEither<R, E, B> {
+  return chain(a => fromEither(f(a)))
+}
+
+/**
+ * @since 2.4.0
+ */
+export function chainIOEither<E, A, B>(
+  f: (a: A) => IOEither<E, B>
+): <R>(ma: ReaderTaskEither<R, E, A>) => ReaderTaskEither<R, E, B> {
+  return chain(a => fromIOEither(f(a)))
+}
+
+/**
+ * @since 2.4.0
+ */
+export function chainTaskEither<E, A, B>(
+  f: (a: A) => TaskEither<E, B>
+): <R>(ma: ReaderTaskEither<R, E, A>) => ReaderTaskEither<R, E, B> {
+  return chain(a => fromTaskEither(f(a)))
+}
+
 /**
  * @since 2.0.0
  */
diff --git a/src/StateReaderTaskEither.ts b/src/StateReaderTaskEither.ts
index c34ebf5f..eae4dae9 100644
--- a/src/StateReaderTaskEither.ts
+++ b/src/StateReaderTaskEither.ts
@@ -187,6 +187,42 @@ export const modify: <S, R, E = never>(f: (s: S) => S) => StateReaderTaskEither<
  */
 export const gets: <S, R, E = never, A = never>(f: (s: S) => A) => StateReaderTaskEither<S, R, E, A> = T.gets
 
+/**
+ * @since 2.4.0
+ */
+export function chainEither<E, A, B>(
+  f: (a: A) => Either<E, B>
+): <S, R>(ma: StateReaderTaskEither<S, R, E, A>) => StateReaderTaskEither<S, R, E, B> {
+  return chain(a => fromEither(f(a)))
+}
+
+/**
+ * @since 2.4.0
+ */
+export function chainIOEither<E, A, B>(
+  f: (a: A) => IOEither<E, B>
+): <S, R>(ma: StateReaderTaskEither<S, R, E, A>) => StateReaderTaskEither<S, R, E, B> {
+  return chain(a => fromIOEither(f(a)))
+}
+
+/**
+ * @since 2.4.0
+ */
+export function chainTaskEither<E, A, B>(
+  f: (a: A) => TaskEither<E, B>
+): <S, R>(ma: StateReaderTaskEither<S, R, E, A>) => StateReaderTaskEither<S, R, E, B> {
+  return chain(a => fromTaskEither(f(a)))
+}
+
+/**
+ * @since 2.4.0
+ */
+export function chainReaderTaskEither<R, E, A, B>(
+  f: (a: A) => ReaderTaskEither<R, E, B>
+): <S>(ma: StateReaderTaskEither<S, R, E, A>) => StateReaderTaskEither<S, R, E, B> {
+  return chain(a => fromReaderTaskEither(f(a)))
+}
+
 /**
  * @since 2.0.0
  */
diff --git a/src/Task.ts b/src/Task.ts
index 38e280c1..4a91baff 100644
--- a/src/Task.ts
+++ b/src/Task.ts
@@ -114,6 +114,13 @@ export function of<A>(a: A): Task<A> {
   return () => Promise.resolve(a)
 }
 
+/**
+ * @since 2.4.0
+ */
+export function chainIO<A, B>(f: (a: A) => IO<B>): (ma: Task<A>) => Task<B> {
+  return chain(a => fromIO(f(a)))
+}
+
 /**
  * @since 2.0.0
  */
diff --git a/src/TaskEither.ts b/src/TaskEither.ts
index d3fb543a..a275c723 100644
--- a/src/TaskEither.ts
+++ b/src/TaskEither.ts
@@ -266,6 +266,20 @@ export function getFilterable<E>(M: Monoid<E>): Filterable2C<URI, E> {
   }
 }
 
+/**
+ * @since 2.4.0
+ */
+export function chainEither<E, A, B>(f: (a: A) => Either<E, B>): (ma: TaskEither<E, A>) => TaskEither<E, B> {
+  return chain(a => fromEither(f(a)))
+}
+
+/**
+ * @since 2.4.0
+ */
+export function chainIOEither<E, A, B>(f: (a: A) => IOEither<E, B>): (ma: TaskEither<E, A>) => TaskEither<E, B> {
+  return chain(a => fromIOEither(f(a)))
+}
+
 /**
  * @since 2.0.0
  */


Test patch:
diff --git a/test/IOEither.ts b/test/IOEither.ts
index 433c62ef..dc9a117a 100644
--- a/test/IOEither.ts
+++ b/test/IOEither.ts
@@ -310,4 +310,10 @@ describe('IOEither', () => {
       assert.deepStrictEqual(r3(), _.left(['a'])())
     })
   })
+
+  it('chainEither', () => {
+    const f = (s: string) => E.right(s.length)
+    const x = pipe(_.right('a'), _.chainEither(f))()
+    assert.deepStrictEqual(x, E.right(1))
+  })
 })
diff --git a/test/ReaderEither.ts b/test/ReaderEither.ts
index 4b1df85a..08a12dc4 100644
--- a/test/ReaderEither.ts
+++ b/test/ReaderEither.ts
@@ -130,4 +130,10 @@ describe('ReaderEither', () => {
       assert.deepStrictEqual(e4, E.left('ab'))
     })
   })
+
+  it('chainEither', () => {
+    const f = (s: string) => E.right(s.length)
+    const x = pipe(_.right('a'), _.chainEither(f))(undefined)
+    assert.deepStrictEqual(x, E.right(1))
+  })
 })
diff --git a/test/ReaderTask.ts b/test/ReaderTask.ts
index fd4ef25a..0fa030cf 100644
--- a/test/ReaderTask.ts
+++ b/test/ReaderTask.ts
@@ -3,7 +3,8 @@ import { array } from '../src/Array'
 import { pipe } from '../src/pipeable'
 import { reader } from '../src/Reader'
 import * as _ from '../src/ReaderTask'
-import { task } from '../src/Task'
+import * as T from '../src/Task'
+import * as I from '../src/IO'
 import { monoidString } from '../src/Monoid'
 import { semigroupString } from '../src/Semigroup'
 
@@ -59,7 +60,7 @@ describe('ReaderTask', () => {
   })
 
   it('fromTask', async () => {
-    const e = await _.fromTask(task.of(1))({})()
+    const e = await _.fromTask(T.of(1))({})()
     assert.deepStrictEqual(e, 1)
   })
 
@@ -117,4 +118,16 @@ describe('ReaderTask', () => {
       assert.deepStrictEqual(e, 1)
     })
   })
+
+  it('chainIO', async () => {
+    const f = (s: string) => I.of(s.length)
+    const x = await _.run(pipe(_.of('a'), _.chainIO(f)), undefined)
+    assert.strictEqual(x, 1)
+  })
+
+  it('chainTask', async () => {
+    const f = (s: string) => T.of(s.length)
+    const x = await _.run(pipe(_.of('a'), _.chainTask(f)), undefined)
+    assert.strictEqual(x, 1)
+  })
 })
diff --git a/test/ReaderTaskEither.ts b/test/ReaderTaskEither.ts
index 72c7114f..e86064e2 100644
--- a/test/ReaderTaskEither.ts
+++ b/test/ReaderTaskEither.ts
@@ -2,15 +2,16 @@ import * as assert from 'assert'
 import { array } from '../src/Array'
 import * as E from '../src/Either'
 import { io } from '../src/IO'
+import * as IE from '../src/IOEither'
 import { monoidSum } from '../src/Monoid'
 import { none, some } from '../src/Option'
 import { pipe } from '../src/pipeable'
 import { reader } from '../src/Reader'
+import * as RE from '../src/ReaderEither'
 import * as _ from '../src/ReaderTaskEither'
-import { semigroupSum, semigroupString } from '../src/Semigroup'
+import { semigroupString, semigroupSum } from '../src/Semigroup'
 import { task } from '../src/Task'
-import { taskEither } from '../src/TaskEither'
-import * as RE from '../src/ReaderEither'
+import * as TE from '../src/TaskEither'
 
 describe('ReaderTaskEither', () => {
   describe('Monad', () => {
@@ -103,7 +104,7 @@ describe('ReaderTaskEither', () => {
   })
 
   it('fromTaskEither', async () => {
-    const e = await _.run(_.fromTaskEither(taskEither.of(1)), {})
+    const e = await _.run(_.fromTaskEither(TE.taskEither.of(1)), {})
     assert.deepStrictEqual(e, E.right(1))
   })
 
@@ -454,4 +455,22 @@ describe('ReaderTaskEither', () => {
       assert.deepStrictEqual(e, E.left('release failure'))
     })
   })
+
+  it('chainEither', async () => {
+    const f = (s: string) => E.right(s.length)
+    const x = await _.run(pipe(_.right('a'), _.chainEither(f)), undefined)
+    assert.deepStrictEqual(x, E.right(1))
+  })
+
+  it('chainIOEither', async () => {
+    const f = (s: string) => IE.right(s.length)
+    const x = await _.run(pipe(_.right('a'), _.chainIOEither(f)), undefined)
+    assert.deepStrictEqual(x, E.right(1))
+  })
+
+  it('chainTaskEither', async () => {
+    const f = (s: string) => TE.right(s.length)
+    const x = await _.run(pipe(_.right('a'), _.chainTaskEither(f)), undefined)
+    assert.deepStrictEqual(x, E.right(1))
+  })
 })
diff --git a/test/StateReaderTaskEither.ts b/test/StateReaderTaskEither.ts
index f24d9143..71978a34 100644
--- a/test/StateReaderTaskEither.ts
+++ b/test/StateReaderTaskEither.ts
@@ -1,15 +1,16 @@
 import * as assert from 'assert'
 import * as E from '../src/Either'
+import { io } from '../src/IO'
 import * as IE from '../src/IOEither'
 import * as O from '../src/Option'
+import { pipe } from '../src/pipeable'
 import { reader } from '../src/Reader'
+import * as RE from '../src/ReaderEither'
 import * as RTE from '../src/ReaderTaskEither'
 import { State } from '../src/State'
 import * as _ from '../src/StateReaderTaskEither'
 import { task } from '../src/Task'
-import { taskEither } from '../src/TaskEither'
-import { io } from '../src/IO'
-import * as RE from '../src/ReaderEither'
+import * as TE from '../src/TaskEither'
 
 describe('StateReaderTaskEither', () => {
   it('run', async () => {
@@ -91,7 +92,7 @@ describe('StateReaderTaskEither', () => {
   })
 
   it('fromTaskEither', async () => {
-    const e = await _.run(_.fromTaskEither(taskEither.of(1)), {}, {})
+    const e = await _.run(_.fromTaskEither(TE.taskEither.of(1)), {}, {})
     assert.deepStrictEqual(e, E.right([1, {}]))
   })
 
@@ -149,4 +150,28 @@ describe('StateReaderTaskEither', () => {
     const e2 = await _.run(_.fromReaderEither(RE.right(1)), {}, {})
     assert.deepStrictEqual(e2, E.right([1, {}]))
   })
+
+  it('chainEither', async () => {
+    const f = (s: string) => E.right(s.length)
+    const x = await _.run(pipe(_.right('a'), _.chainEither(f)), undefined, undefined)
+    assert.deepStrictEqual(x, E.right([1, undefined]))
+  })
+
+  it('chainIOEither', async () => {
+    const f = (s: string) => IE.right(s.length)
+    const x = await _.run(pipe(_.right('a'), _.chainIOEither(f)), undefined, undefined)
+    assert.deepStrictEqual(x, E.right([1, undefined]))
+  })
+
+  it('chainTaskEither', async () => {
+    const f = (s: string) => TE.right(s.length)
+    const x = await _.run(pipe(_.right('a'), _.chainTaskEither(f)), undefined, undefined)
+    assert.deepStrictEqual(x, E.right([1, undefined]))
+  })
+
+  it('chainReaderTaskEither', async () => {
+    const f = (s: string) => RTE.right(s.length)
+    const x = await _.run(pipe(_.right('a'), _.chainReaderTaskEither(f)), undefined, undefined)
+    assert.deepStrictEqual(x, E.right([1, undefined]))
+  })
 })
diff --git a/test/Task.ts b/test/Task.ts
index 174fdfcb..3bac39ff 100644
--- a/test/Task.ts
+++ b/test/Task.ts
@@ -1,18 +1,20 @@
 import * as assert from 'assert'
 import { array } from '../src/Array'
+import * as I from '../src/IO'
 import { monoidString } from '../src/Monoid'
-import * as T from '../src/Task'
+import { pipe } from '../src/pipeable'
+import * as _ from '../src/Task'
 
-const delayReject = <A>(n: number, a: A): T.Task<A> => () =>
+const delayReject = <A>(n: number, a: A): _.Task<A> => () =>
   new Promise<A>((_, reject) => {
     setTimeout(() => reject(a), n)
   })
 
-const delay = <A>(millis: number, a: A): T.Task<A> => T.delay(millis)(T.task.of(a))
+const delay = <A>(millis: number, a: A): _.Task<A> => _.delay(millis)(_.task.of(a))
 
 describe('Task', () => {
   describe('getRaceMonoid', () => {
-    const M = T.getRaceMonoid<number>()
+    const M = _.getRaceMonoid<number>()
 
     it('concat', async () => {
       const x = await M.concat(delay(10, 1), delay(10, 2))()
@@ -39,7 +41,7 @@ describe('Task', () => {
   })
 
   describe('getMonoid', () => {
-    const M = T.getMonoid(monoidString)
+    const M = _.getMonoid(monoidString)
 
     it('concat', async () => {
       const x = await M.concat(delay(10, 'a'), delay(10, 'b'))()
@@ -60,7 +62,7 @@ describe('Task', () => {
   describe('Monad', () => {
     it('map', async () => {
       const double = (n: number): number => n * 2
-      const x = await T.task.map(delay(0, 1), double)()
+      const x = await _.task.map(delay(0, 1), double)()
       assert.strictEqual(x, 2)
     })
 
@@ -68,13 +70,13 @@ describe('Task', () => {
       const double = (n: number): number => n * 2
       const tab = delay(0, double)
       const ta = delay(0, 1)
-      const x = await T.task.ap(tab, ta)()
+      const x = await _.task.ap(tab, ta)()
       assert.strictEqual(x, 2)
     })
 
     it('chain', async () => {
-      const f = (n: number): T.Task<number> => () => Promise.resolve(n * 2)
-      const x = await T.task.chain(delay(0, 1), f)()
+      const f = (n: number): _.Task<number> => () => Promise.resolve(n * 2)
+      const x = await _.task.chain(delay(0, 1), f)()
       return assert.strictEqual(x, 2)
     })
   })
@@ -82,10 +84,10 @@ describe('Task', () => {
   describe('Traversable', () => {
     it('sequence parallel', async () => {
       const log: Array<string> = []
-      const append = (message: string): T.Task<number> => () => Promise.resolve(log.push(message))
-      const t1 = T.task.chain(append('start 1'), () => append('end 1'))
-      const t2 = T.task.chain(append('start 2'), () => append('end 2'))
-      const sequenceParallel = array.sequence(T.task)
+      const append = (message: string): _.Task<number> => () => Promise.resolve(log.push(message))
+      const t1 = _.task.chain(append('start 1'), () => append('end 1'))
+      const t2 = _.task.chain(append('start 2'), () => append('end 2'))
+      const sequenceParallel = array.sequence(_.task)
       const x = await sequenceParallel([t1, t2])()
       assert.deepStrictEqual(x, [3, 4])
       assert.deepStrictEqual(log, ['start 1', 'start 2', 'end 1', 'end 2'])
@@ -93,10 +95,10 @@ describe('Task', () => {
 
     it('sequence series', async () => {
       const log: Array<string> = []
-      const append = (message: string): T.Task<number> => () => Promise.resolve(log.push(message))
-      const t1 = T.task.chain(append('start 1'), () => append('end 1'))
-      const t2 = T.task.chain(append('start 2'), () => append('end 2'))
-      const sequenceSeries = array.sequence(T.taskSeq)
+      const append = (message: string): _.Task<number> => () => Promise.resolve(log.push(message))
+      const t1 = _.task.chain(append('start 1'), () => append('end 1'))
+      const t2 = _.task.chain(append('start 2'), () => append('end 2'))
+      const sequenceSeries = array.sequence(_.taskSeq)
       const x = await sequenceSeries([t1, t2])()
       assert.deepStrictEqual(x, [2, 4])
       assert.deepStrictEqual(log, ['start 1', 'end 1', 'start 2', 'end 2'])
@@ -106,7 +108,7 @@ describe('Task', () => {
   describe('MonadIO', () => {
     it('fromIO', async () => {
       const io = () => 1
-      const task = T.task.fromIO(io)
+      const task = _.task.fromIO(io)
       const x = await task()
       assert.strictEqual(x, 1)
     })
@@ -115,8 +117,14 @@ describe('Task', () => {
   describe('MonadTask', () => {
     it('fromTask', async () => {
       const io = () => 1
-      const t = T.task.fromIO(io)
-      assert.strictEqual(T.task.fromTask(t), t)
+      const t = _.task.fromIO(io)
+      assert.strictEqual(_.task.fromTask(t), t)
     })
   })
+
+  it('chainIO', async () => {
+    const f = (s: string) => I.of(s.length)
+    const x = await pipe(_.of('a'), _.chainIO(f))()
+    assert.strictEqual(x, 1)
+  })
 })
diff --git a/test/TaskEither.ts b/test/TaskEither.ts
index 0609dabb..382161ff 100644
--- a/test/TaskEither.ts
+++ b/test/TaskEither.ts
@@ -7,6 +7,7 @@ import { none, some } from '../src/Option'
 import { pipe, pipeable } from '../src/pipeable'
 import { semigroupString, semigroupSum } from '../src/Semigroup'
 import * as _ from '../src/TaskEither'
+import { ioEither } from '../src/IOEither'
 
 describe('TaskEither', () => {
   describe('Monad', () => {
@@ -362,4 +363,16 @@ describe('TaskEither', () => {
       assert.deepStrictEqual(await r3(), await _.left(['a'])())
     })
   })
+
+  it('chainEither', async () => {
+    const f = (s: string) => E.right(s.length)
+    const x = await pipe(_.right('a'), _.chainEither(f))()
+    assert.deepStrictEqual(x, E.right(1))
+  })
+
+  it('chainIOEither', async () => {
+    const f = (s: string) => ioEither.of(s.length)
+    const x = await pipe(_.right('a'), _.chainIOEither(f))()
+    assert.deepStrictEqual(x, E.right(1))
+  })
 })