Repository: gcanti/fp-ts
Pull request #810 (2019-04-03 07:27:22)

New Feature: add MonadThrow


Patch:
diff --git a/docs/basics/index.md b/docs/basics/index.md
index d939d0aa..96147ff3 100644
--- a/docs/basics/index.md
+++ b/docs/basics/index.md
@@ -11,6 +11,5 @@ has_toc: false
 The basic tutorial is a work in progress. It will be directed towards beginners once it's ready.
 {: .fs-6 .fw-300 }
 
-In the meantime, check out the [learning resources](../introduction/learning-resources) and the following code example:
+In the meantime, check out the [learning resources](../introduction/learning-resources).
 
-- [Debugging using the `Trace` module (code)](https://github.com/gcanti/fp-ts/blob/master/tutorials/debugging-with-Trace.ts)
diff --git a/docs/modules/Apply.ts.md b/docs/modules/Apply.ts.md
index 3b6fc270..29b536ef 100644
--- a/docs/modules/Apply.ts.md
+++ b/docs/modules/Apply.ts.md
@@ -360,7 +360,7 @@ Added in v1.15.0
 
 # sequenceT (function)
 
-Tuple sequencing, i.e., take a tuple of monadic actions and do them from left-to-right, returning the resulting tuple.
+Tuple sequencing, i.e., take a tuple of actions and do them from left-to-right, returning the resulting tuple.
 
 **Signature**
 
diff --git a/docs/modules/Trace.ts.md b/docs/modules/Trace.ts.md
index ca86afc2..dbf692ac 100644
--- a/docs/modules/Trace.ts.md
+++ b/docs/modules/Trace.ts.md
@@ -39,7 +39,7 @@ representation for low-level debugging
 **Signature**
 
 ```ts
-export const trace = <A>(message: any, out: Lazy<A>): A => ...
+export const trace = <A>(message: unknown, out: Lazy<A>): A => ...
 ```
 
 Added in v1.0.0
@@ -51,11 +51,11 @@ Log a message to the console for debugging purposes and then return the unit val
 **Signature**
 
 ```ts
-export function traceA<F extends URIS3>(F: Applicative3<F>): <U, L>(message: any) => Type3<F, U, L, void>
-export function traceA<F extends URIS3, U, L>(F: Applicative3C<F, U, L>): (message: any) => Type3<F, U, L, void>
-export function traceA<F extends URIS2>(F: Applicative2<F>): <L>(message: any) => Type2<F, L, void>
-export function traceA<F extends URIS2, L>(F: Applicative2C<F, L>): (message: any) => Type2<F, L, void>
-export function traceA<F extends URIS>(F: Applicative1<F>): (message: any) => Type<F, void> { ... }
+export function traceA<F extends URIS3>(F: Applicative3<F>): <U, L>(message: unknown) => Type3<F, U, L, void>
+export function traceA<F extends URIS3, U, L>(F: Applicative3C<F, U, L>): (message: unknown) => Type3<F, U, L, void>
+export function traceA<F extends URIS2>(F: Applicative2<F>): <L>(message: unknown) => Type2<F, L, void>
+export function traceA<F extends URIS2, L>(F: Applicative2C<F, L>): (message: unknown) => Type2<F, L, void>
+export function traceA<F extends URIS>(F: Applicative1<F>): (message: unknown) => Type<F, void> { ... }
 ```
 
 Added in v1.0.0
diff --git a/docs/modules/function.ts.md b/docs/modules/function.ts.md
index 7a39af1f..4a0d3c8f 100644
--- a/docs/modules/function.ts.md
+++ b/docs/modules/function.ts.md
@@ -28,6 +28,7 @@ parent: Modules
 - [Function7 (type alias)](#function7-type-alias)
 - [Function8 (type alias)](#function8-type-alias)
 - [Function9 (type alias)](#function9-type-alias)
+- [FunctionN (type alias)](#functionn-type-alias)
 - [Kleisli (type alias)](#kleisli-type-alias)
 - [Lazy (type alias)](#lazy-type-alias)
 - [Predicate (type alias)](#predicate-type-alias)
@@ -226,6 +227,14 @@ export type Function8<A, B, C, D, E, F, G, H, I> = (a: A, b: B, c: C, d: D, e: E
 export type Function9<A, B, C, D, E, F, G, H, I, J> = (a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I) => J
 ```
 
+# FunctionN (type alias)
+
+**Signature**
+
+```ts
+export type FunctionN<A extends Array<any>, B> = (...args: A) => B
+```
+
 # Kleisli (type alias)
 
 **Signature**
diff --git a/docs/recipes/debugging.md b/docs/recipes/debugging.md
new file mode 100644
index 00000000..c0ba3bb4
--- /dev/null
+++ b/docs/recipes/debugging.md
@@ -0,0 +1,91 @@
+---
+title: Debugging using the Trace module
+parent: Recipes
+nav_order: 9
+---
+
+# Debugging using the `Trace` module
+
+The module `'fp-ts/lib/Trace'` contains a bunch of helpers which make `console` based debugging a lot easier.
+
+## How to log a value to the console
+
+Say you want to log the value of `s` in the following snippet
+
+```ts
+import { head } from 'fp-ts/lib/Array'
+
+export const len = (s: string): number => s.length
+
+export const x = head(['a', 'bb', 'ccc']).map(s => len(s))
+```
+
+You can use the `spy` helper
+
+```ts
+import { head } from 'fp-ts/lib/Array'
+import { spy } from 'fp-ts/lib/Trace'
+
+const len = (s: string): number => s.length
+
+const x = head(['a', 'bb', 'ccc']).map(s => len(spy(s)))
+// => 'a'
+```
+
+## How to log a value before returning another value
+
+You can use the `trace` helper
+
+```ts
+import { head } from 'fp-ts/lib/Array'
+import { spy, trace } from 'fp-ts/lib/Trace'
+
+const len = (s: string): number => spy(s.length)
+
+const x = head(['a', 'bb', 'ccc']).map(s =>
+  trace('The value before calling `len` is: ' + s, () => len(s))
+)
+// => The value before calling `len` is: a
+// => 1
+```
+
+The first message is from `trace`, the second message is from `spy`.
+
+## How to log a value in an `Applicative` context
+
+You can use the `traceA` helper
+
+```ts
+import { option, some } from 'fp-ts/lib/Option'
+import { traceA } from 'fp-ts/lib/Trace'
+
+// debug returns an `Option<void>`
+const debug = traceA(option)
+
+debug('start computation')
+  .chain(() => some(1))
+  .chain(() => debug('end computation'))
+// => start computation
+// => end computation
+```
+
+## How to log a value in a `Monad` context
+
+This is especially useful when one has monadic chains.
+
+You can use the `traceM` helper
+
+```ts
+import { head } from 'fp-ts/lib/Array'
+import { option, some } from 'fp-ts/lib/Option'
+import { traceM } from 'fp-ts/lib/Trace'
+
+const debug = traceM(option)
+
+some([1, 2, 3])
+  .chain(debug)
+  .chain(head)
+  .chain(debug)
+// => [1, 2, 3]
+// => 1
+```
diff --git a/src/Apply.ts b/src/Apply.ts
index 7a15c006..3dfbeb90 100644
--- a/src/Apply.ts
+++ b/src/Apply.ts
@@ -10,10 +10,10 @@
  *
  * Formally, `Apply` represents a strong lax semi-monoidal endofunctor.
  */
+import { constant, curried, Curried2, Curried3, Curried4, Function1 } from './function'
 import { Functor, Functor1, Functor2, Functor2C, Functor3, Functor3C } from './Functor'
 import { HKT, Type, Type2, Type3, URIS, URIS2, URIS3 } from './HKT'
 import { Semigroup } from './Semigroup'
-import { Curried2, Curried3, Curried4, constant, curried, Function1 } from './function'
 
 /**
  * @since 1.0.0
@@ -234,7 +234,7 @@ export function getSemigroup<F, A>(F: Apply<F>, S: Semigroup<A>): () => Semigrou
 const tupleConstructors: { [key: string]: Function1<any, any> } = {}
 
 /**
- * Tuple sequencing, i.e., take a tuple of monadic actions and do them from left-to-right, returning the resulting tuple.
+ * Tuple sequencing, i.e., take a tuple of actions and do them from left-to-right, returning the resulting tuple.
  *
  * @example
  * import { sequenceT } from 'fp-ts/lib/Apply'
diff --git a/src/Either.ts b/src/Either.ts
index 5cf39ec1..6817a790 100644
--- a/src/Either.ts
+++ b/src/Either.ts
@@ -33,7 +33,7 @@ import { Compactable2C, Separated } from './Compactable'
 import { Extend2 } from './Extend'
 import { Filterable2C } from './Filterable'
 import { Foldable2v2 } from './Foldable2v'
-import { Lazy, phantom, Predicate, Refinement, toString } from './function'
+import { Lazy, phantom, Predicate, Refinement, toString, identity } from './function'
 import { HKT } from './HKT'
 import { Monad2 } from './Monad'
 import { Monoid } from './Monoid'
@@ -43,6 +43,7 @@ import { Setoid, fromEquals } from './Setoid'
 import { Traversable2v2 } from './Traversable2v'
 import { Validation } from './Validation'
 import { Witherable2C } from './Witherable'
+import { MonadThrow2 } from './MonadThrow'
 
 declare module './HKT' {
   interface URI2HKT2<L, A> {
@@ -281,6 +282,7 @@ export const getSetoid = <L, A>(SL: Setoid<L>, SA: Setoid<A>): Setoid<Either<L,
     (x, y) => (x.isLeft() ? y.isLeft() && SL.equals(x.value, y.value) : y.isRight() && SA.equals(x.value, y.value))
   )
 }
+
 /**
  * Semigroup returning the left-most non-`Left` value. If both operands are `Right`s then the inner values are
  * appended using the provided `Semigroup`
@@ -692,6 +694,10 @@ export function getWitherable<L>(ML: Monoid<L>): Witherable2C<URI, L> {
   }
 }
 
+const throwError = left
+
+const fromEither = identity
+
 /**
  * @since 1.0.0
  */
@@ -701,7 +707,8 @@ export const either: Monad2<URI> &
   Bifunctor2<URI> &
   Alt2<URI> &
   Extend2<URI> &
-  ChainRec2<URI> = {
+  ChainRec2<URI> &
+  MonadThrow2<URI> = {
   URI,
   map,
   of,
@@ -715,5 +722,8 @@ export const either: Monad2<URI> &
   bimap,
   alt,
   extend,
-  chainRec
+  chainRec,
+  throwError,
+  fromEither,
+  fromOption: (o, e) => (o.isNone() ? throwError(e) : of(o.value))
 }
diff --git a/src/IOEither.ts b/src/IOEither.ts
index 4c948404..85f7ec0c 100644
--- a/src/IOEither.ts
+++ b/src/IOEither.ts
@@ -2,13 +2,14 @@
  * @file `IOEither<L, A>` represents a synchronous computation that either yields a value of type `A` or fails yielding an
  * error of type `L`. If you want to represent a synchronous computation that never fails, please see `IO`.
  */
+import { Alt2 } from './Alt'
 import { Bifunctor2 } from './Bifunctor'
-import { Either, left as eitherLeft, right as eitherRight, tryCatch2v as eitherTryCatch2v, toError } from './Either'
+import { Either, left as eitherLeft, right as eitherRight, toError, tryCatch2v as eitherTryCatch2v } from './Either'
 import * as eitherT from './EitherT'
-import { Monad2 } from './Monad'
+import { constant, constIdentity, Lazy } from './function'
 import { IO, io } from './IO'
-import { Lazy, constIdentity, constant } from './function'
-import { Alt2 } from './Alt'
+import { Monad2 } from './Monad'
+import { MonadThrow2 } from './MonadThrow'
 
 declare module './HKT' {
   interface URI2HKT2<L, A> {
@@ -150,15 +151,20 @@ export const tryCatch2v = <L, A>(f: Lazy<A>, onerror: (reason: unknown) => L): I
   return new IOEither(new IO(() => eitherTryCatch2v(f, onerror)))
 }
 
+const throwError = fromLeft
+
 /**
  * @since 1.6.0
  */
-export const ioEither: Monad2<URI> & Bifunctor2<URI> & Alt2<URI> = {
+export const ioEither: Monad2<URI> & Bifunctor2<URI> & Alt2<URI> & MonadThrow2<URI> = {
   URI,
   bimap,
   map,
   of,
   ap,
   chain,
-  alt
+  alt,
+  throwError,
+  fromEither,
+  fromOption: (o, e) => (o.isNone() ? throwError(e) : of(o.value))
 }
diff --git a/src/MonadThrow.ts b/src/MonadThrow.ts
new file mode 100644
index 00000000..0084cb64
--- /dev/null
+++ b/src/MonadThrow.ts
@@ -0,0 +1,49 @@
+import { HKT2, Type2, Type3, URIS2, URIS3 } from './HKT'
+import { Monad2, Monad3, Monad2C, Monad3C } from './Monad'
+import { Either } from './Either'
+import { Option } from './Option'
+
+/**
+ * The `MonadThrow` type class represents those monads which support errors via
+ * `throwError`, where `throwError(e)` halts, yielding the error `e`.
+ *
+ * Laws:
+ *
+ * - Left zero: `M.chain(M.throwError(e), f) = M.throwError(e)`
+ *
+ * @since 1.16.0
+ */
+export interface MonadThrow<M> {
+  readonly URI: M
+  readonly map: <L, A, B>(fa: HKT2<M, L, A>, f: (a: A) => B) => HKT2<M, L, B>
+  readonly ap: <L, A, B>(fab: HKT2<M, L, (a: A) => B>, fa: HKT2<M, L, A>) => HKT2<M, L, B>
+  readonly of: <L, A>(a: A) => HKT2<M, L, A>
+  readonly chain: <L, A, B>(fa: HKT2<M, L, A>, f: (a: A) => HKT2<M, L, B>) => HKT2<M, L, B>
+  readonly throwError: <E, A>(e: E) => HKT2<M, E, A>
+  readonly fromEither: <E, A>(e: Either<E, A>) => HKT2<M, E, A>
+  readonly fromOption: <E, A>(o: Option<A>, e: E) => HKT2<M, E, A>
+}
+
+export interface MonadThrow2<M extends URIS2> extends Monad2<M> {
+  readonly throwError: <E, A>(e: E) => Type2<M, E, A>
+  readonly fromEither: <E, A>(e: Either<E, A>) => Type2<M, E, A>
+  readonly fromOption: <E, A>(o: Option<A>, e: E) => Type2<M, E, A>
+}
+
+export interface MonadThrow2C<M extends URIS2, E> extends Monad2C<M, E> {
+  readonly throwError: <A>(e: E) => Type2<M, E, A>
+  readonly fromEither: <A>(e: Either<E, A>) => Type2<M, E, A>
+  readonly fromOption: <A>(o: Option<A>, e: E) => Type2<M, E, A>
+}
+
+export interface MonadThrow3<M extends URIS3> extends Monad3<M> {
+  readonly throwError: <U, E, A>(e: E) => Type3<M, U, E, A>
+  readonly fromEither: <U, E, A>(e: Either<E, A>) => Type3<M, U, E, A>
+  readonly fromOption: <U, E, A>(o: Option<A>, e: E) => Type3<M, U, E, A>
+}
+
+export interface MonadThrow3C<M extends URIS3, U, E> extends Monad3C<M, U, E> {
+  readonly throwError: <A>(e: E) => Type3<M, U, E, A>
+  readonly fromEither: <A>(e: Either<E, A>) => Type3<M, U, E, A>
+  readonly fromOption: <A>(o: Option<A>, e: E) => Type3<M, U, E, A>
+}
diff --git a/src/ReaderTaskEither.ts b/src/ReaderTaskEither.ts
index 0863ff41..272efbc3 100644
--- a/src/ReaderTaskEither.ts
+++ b/src/ReaderTaskEither.ts
@@ -12,6 +12,7 @@ import { Task } from './Task'
 import * as taskEither from './TaskEither'
 import TaskEither = taskEither.TaskEither
 import { MonadTask3 } from './MonadTask'
+import { MonadThrow3 } from './MonadThrow'
 
 const readerTTaskEither = readerT.getReaderT2v(taskEither.taskEither)
 
@@ -236,10 +237,17 @@ export const tryCatch = <E, L, A>(
 
 const fromTask = right
 
+const throwError = fromLeft
+
 /**
  * @since 1.6.0
  */
-export const readerTaskEither: Monad3<URI> & Bifunctor3<URI> & Alt3<URI> & MonadIO3<URI> & MonadTask3<URI> = {
+export const readerTaskEither: Monad3<URI> &
+  Bifunctor3<URI> &
+  Alt3<URI> &
+  MonadIO3<URI> &
+  MonadTask3<URI> &
+  MonadThrow3<URI> = {
   URI,
   map,
   of,
@@ -248,7 +256,10 @@ export const readerTaskEither: Monad3<URI> & Bifunctor3<URI> & Alt3<URI> & Monad
   alt,
   bimap,
   fromIO,
-  fromTask
+  fromTask,
+  throwError,
+  fromEither,
+  fromOption: (o, e) => (o.isNone() ? throwError(e) : of(o.value))
 }
 
 /**
diff --git a/src/TaskEither.ts b/src/TaskEither.ts
index 797632ee..982616e8 100644
--- a/src/TaskEither.ts
+++ b/src/TaskEither.ts
@@ -18,10 +18,11 @@ import { IO } from './IO'
 import { IOEither } from './IOEither'
 import { Monad2 } from './Monad'
 import { MonadIO2 } from './MonadIO'
+import { MonadTask2 } from './MonadTask'
+import { MonadThrow2 } from './MonadThrow'
 import { Monoid } from './Monoid'
 import { Semigroup } from './Semigroup'
 import { fromIO as taskFromIO, getSemigroup as taskGetSemigroup, Task, task, tryCatch as taskTryCatch } from './Task'
-import { MonadTask2 } from './MonadTask'
 
 declare module './HKT' {
   interface URI2HKT2<L, A> {
@@ -380,10 +381,17 @@ export const bracket = <L, A, B>(
   )
 }
 
+const throwError = fromLeft
+
 /**
  * @since 1.0.0
  */
-export const taskEither: Monad2<URI> & Bifunctor2<URI> & Alt2<URI> & MonadIO2<URI> & MonadTask2<URI> = {
+export const taskEither: Monad2<URI> &
+  Bifunctor2<URI> &
+  Alt2<URI> &
+  MonadIO2<URI> &
+  MonadTask2<URI> &
+  MonadThrow2<URI> = {
   URI,
   bimap,
   map,
@@ -392,7 +400,10 @@ export const taskEither: Monad2<URI> & Bifunctor2<URI> & Alt2<URI> & MonadIO2<UR
   chain,
   alt,
   fromIO,
-  fromTask
+  fromTask,
+  throwError,
+  fromEither,
+  fromOption: (o, e) => (o.isNone() ? throwError(e) : of(o.value))
 }
 
 /**
diff --git a/src/Trace.ts b/src/Trace.ts
index f7c8d9bf..1269ba5a 100644
--- a/src/Trace.ts
+++ b/src/Trace.ts
@@ -12,7 +12,7 @@ import { Lazy } from './function'
  *
  * @since 1.0.0
  */
-export const trace = <A>(message: any, out: Lazy<A>): A => {
+export const trace = <A>(message: unknown, out: Lazy<A>): A => {
   console.log(message) // tslint:disable-line:no-console
   return out()
 }
@@ -31,12 +31,12 @@ export const spy = <A>(a: A): A => {
  *
  * @since 1.0.0
  */
-export function traceA<F extends URIS3>(F: Applicative3<F>): <U, L>(message: any) => Type3<F, U, L, void>
-export function traceA<F extends URIS3, U, L>(F: Applicative3C<F, U, L>): (message: any) => Type3<F, U, L, void>
-export function traceA<F extends URIS2>(F: Applicative2<F>): <L>(message: any) => Type2<F, L, void>
-export function traceA<F extends URIS2, L>(F: Applicative2C<F, L>): (message: any) => Type2<F, L, void>
-export function traceA<F extends URIS>(F: Applicative1<F>): (message: any) => Type<F, void>
-export function traceA<F>(F: Applicative<F>): (message: any) => HKT<F, void> {
+export function traceA<F extends URIS3>(F: Applicative3<F>): <U, L>(message: unknown) => Type3<F, U, L, void>
+export function traceA<F extends URIS3, U, L>(F: Applicative3C<F, U, L>): (message: unknown) => Type3<F, U, L, void>
+export function traceA<F extends URIS2>(F: Applicative2<F>): <L>(message: unknown) => Type2<F, L, void>
+export function traceA<F extends URIS2, L>(F: Applicative2C<F, L>): (message: unknown) => Type2<F, L, void>
+export function traceA<F extends URIS>(F: Applicative1<F>): (message: unknown) => Type<F, void>
+export function traceA<F>(F: Applicative<F>): (message: unknown) => HKT<F, void> {
   return x => trace(x, () => F.of(undefined))
 }
 
diff --git a/src/Validation.ts b/src/Validation.ts
index aef961e7..c6057c72 100644
--- a/src/Validation.ts
+++ b/src/Validation.ts
@@ -22,6 +22,7 @@ import { Semigroup } from './Semigroup'
 import { Setoid, fromEquals } from './Setoid'
 import { Traversable2v2 } from './Traversable2v'
 import { Witherable2C } from './Witherable'
+import { MonadThrow2C } from './MonadThrow'
 
 declare module './HKT' {
   interface URI2HKT2<L, A> {
@@ -551,6 +552,20 @@ export function getWitherable<L>(ML: Monoid<L>): Witherable2C<URI, L> {
   }
 }
 
+const throwError = failure
+
+/**
+ * @since 1.16.0
+ */
+export const getMonadThrow = <L>(S: Semigroup<L>): MonadThrow2C<URI, L> => {
+  return {
+    ...getMonad(S),
+    throwError,
+    fromEither,
+    fromOption: (o, e) => (o.isNone() ? throwError(e) : of(o.value))
+  }
+}
+
 /**
  * @since 1.0.0
  */
diff --git a/src/function.ts b/src/function.ts
index 1f1d5b12..6b75a092 100644
--- a/src/function.ts
+++ b/src/function.ts
@@ -17,6 +17,8 @@ export const unsafeCoerce: <A, B>(a: A) => B = identity as any
  */
 export type Lazy<A> = () => A
 
+export type FunctionN<A extends Array<any>, B> = (...args: A) => B
+
 export type Function1<A, B> = (a: A) => B
 export type Function2<A, B, C> = (a: A, b: B) => C
 export type Function3<A, B, C, D> = (a: A, b: B, c: C) => D
diff --git a/src/index.ts b/src/index.ts
index 5345b4e4..c1e672cd 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -106,6 +106,8 @@ import * as monadIO from './MonadIO'
 export { monadIO }
 import * as monadTask from './MonadTask'
 export { monadTask }
+import * as monadThrow from './MonadThrow'
+export { monadThrow }
 import * as monoid from './Monoid'
 export { monoid }
 import * as monoidal from './Monoidal'
diff --git a/tutorials/debugging-with-Trace.ts b/tutorials/debugging-with-Trace.ts
deleted file mode 100644
index f730f1a3..00000000
--- a/tutorials/debugging-with-Trace.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-import { head } from '../src/Array'
-import { left } from '../src/Either'
-import { Option, option, some } from '../src/Option'
-import { spy, trace, traceA, traceM } from '../src/Trace'
-
-//
-// spy
-//
-
-const foo = left<string, number>('foo')
-const bar = spy(foo.mapLeft(s => s.length))
-// tslint:disable-next-line: no-console
-console.log(bar)
-// => left(3)
-
-//
-// trace
-//
-
-const bar2 = foo.mapLeft(s => trace('mapping the left side', () => s.length))
-// tslint:disable-next-line: no-console
-console.log(bar2)
-// => 'mapping the left side'
-
-//
-// traceA
-//
-
-const traceAOption = traceA(option)
-
-traceAOption('start computation')
-  .chain(() => some(1))
-  .chain(() => traceAOption('end computation'))
-// => start computation
-// => end computation
-
-//
-// traceM
-//
-
-const traceMOption = traceM(option)
-
-const baz: Option<number> = some([1, 2, 3])
-  .chain(traceMOption)
-  .chain(head)
-  .chain(traceMOption)
-// tslint:disable-next-line: no-console
-console.log(baz)
-// => [1, 2, 3]
-// => 1


Test patch:
diff --git a/test/Either.ts b/test/Either.ts
index 0444e002..5b2a9569 100644
--- a/test/Either.ts
+++ b/test/Either.ts
@@ -488,4 +488,15 @@ describe('Either', () => {
     assert.deepStrictEqual(S.concat(right(1), S.empty), right(1))
     assert.deepStrictEqual(S.concat(S.empty, right(2)), right(2))
   })
+
+  describe('MonadThrow', () => {
+    it('should obey the law', () => {
+      assert.deepStrictEqual(either.chain(either.throwError('error'), a => either.of(a)), either.throwError('error'))
+    })
+
+    it('fromOption', () => {
+      assert.deepStrictEqual(either.fromOption(none, 'error'), left('error'))
+      assert.deepStrictEqual(either.fromOption(some(1), 'error'), right(1))
+    })
+  })
 })
diff --git a/test/IOEither.ts b/test/IOEither.ts
index 98a54d24..db21c593 100644
--- a/test/IOEither.ts
+++ b/test/IOEither.ts
@@ -2,6 +2,7 @@ import * as assert from 'assert'
 import { left as eitherLeft, right as eitherRight } from '../src/Either'
 import { IO, io } from '../src/IO'
 import { IOEither, fromLeft, left, right, ioEither, tryCatch } from '../src/IOEither'
+import { none, some } from '../src/Option'
 
 describe('IOEither', () => {
   it('ap', () => {
@@ -156,4 +157,18 @@ describe('IOEither', () => {
     assert.deepStrictEqual(e, eitherRight(1))
     assert.deepStrictEqual(log, ['a', 'b'])
   })
+
+  describe('MonadThrow', () => {
+    it('should obey the law', () => {
+      assert.deepStrictEqual(
+        ioEither.chain(ioEither.throwError('error'), a => ioEither.of(a)).run(),
+        ioEither.throwError('error').run()
+      )
+    })
+
+    it('fromOption', () => {
+      assert.deepStrictEqual(ioEither.fromOption(none, 'error').run(), eitherLeft('error'))
+      assert.deepStrictEqual(ioEither.fromOption(some(1), 'error').run(), eitherRight(1))
+    })
+  })
 })
diff --git a/test/ReaderTaskEither.ts b/test/ReaderTaskEither.ts
index c7a901ab..2731e1f9 100644
--- a/test/ReaderTaskEither.ts
+++ b/test/ReaderTaskEither.ts
@@ -25,6 +25,7 @@ import { IO } from '../src/IO'
 import { IOEither } from '../src/IOEither'
 import { sequence } from '../src/Traversable'
 import { array } from '../src/Array'
+import { none, some } from '../src/Option'
 
 describe('ReaderTaskEither', () => {
   it('ap', () => {
@@ -305,4 +306,25 @@ describe('ReaderTaskEither', () => {
         assert.deepStrictEqual(log, ['start 1', 'end 1', 'start 2', 'end 2'])
       })
   })
+
+  describe('MonadThrow', () => {
+    it('should obey the law', () => {
+      return Promise.all([
+        readerTaskEither.chain(readerTaskEither.throwError('error'), a => readerTaskEither.of(a)).run({}),
+        readerTaskEither.throwError('error').run({})
+      ]).then(([e1, e2]) => {
+        assert.deepStrictEqual(e1, e2)
+      })
+    })
+
+    it('fromOption', () => {
+      return Promise.all([
+        readerTaskEither.fromOption(none, 'error').run({}),
+        readerTaskEither.fromOption(some(1), 'error').run({})
+      ]).then(([e1, e2]) => {
+        assert.deepStrictEqual(e1, eitherLeft('error'))
+        assert.deepStrictEqual(e2, eitherRight(1))
+      })
+    })
+  })
 })
diff --git a/test/TaskEither.ts b/test/TaskEither.ts
index f002627e..fd57c90f 100644
--- a/test/TaskEither.ts
+++ b/test/TaskEither.ts
@@ -23,6 +23,7 @@ import { monoidString } from '../src/Monoid'
 import { semigroupSum } from '../src/Semigroup'
 import { sequence } from '../src/Traversable'
 import { array } from '../src/Array'
+import { none, some } from '../src/Option'
 
 describe('TaskEither', () => {
   it('attempt', () => {
@@ -506,4 +507,25 @@ describe('TaskEither', () => {
       assert.deepStrictEqual(r5, eitherRight(12))
     })
   })
+
+  describe('MonadThrow', () => {
+    it('should obey the law', () => {
+      return Promise.all([
+        taskEither.chain(taskEither.throwError('error'), a => taskEither.of(a)).run(),
+        taskEither.throwError('error').run()
+      ]).then(([e1, e2]) => {
+        assert.deepStrictEqual(e1, e2)
+      })
+    })
+
+    it('fromOption', () => {
+      return Promise.all([
+        taskEither.fromOption(none, 'error').run(),
+        taskEither.fromOption(some(1), 'error').run()
+      ]).then(([e1, e2]) => {
+        assert.deepStrictEqual(e1, eitherLeft('error'))
+        assert.deepStrictEqual(e2, eitherRight(1))
+      })
+    })
+  })
 })
diff --git a/test/Validation.ts b/test/Validation.ts
index 12ac55c6..c367096a 100644
--- a/test/Validation.ts
+++ b/test/Validation.ts
@@ -24,7 +24,8 @@ import {
   isSuccess,
   success,
   validation,
-  tryCatch
+  tryCatch,
+  getMonadThrow
 } from '../src/Validation'
 
 const p = (n: number): boolean => n > 2
@@ -341,4 +342,17 @@ describe('Validation', () => {
     )
     assert.deepStrictEqual(v2, success(1))
   })
+
+  describe('getMonadThrow', () => {
+    const M = getMonadThrow(monoidString)
+
+    it('should obey the law', () => {
+      assert.deepStrictEqual(M.chain(M.throwError('error'), a => M.of(a)), M.throwError('error'))
+    })
+
+    it('fromOption', () => {
+      assert.deepStrictEqual(M.fromOption(none, 'error'), failure('error'))
+      assert.deepStrictEqual(M.fromOption(some(1), 'error'), success(1))
+    })
+  })
 })