Repository: gcanti/fp-ts
Pull request #783 (2019-03-23 15:36:58)

Add new Function alias
This is a feature request to add new alias for Function.

`Function<A,B>` accepts functions has arguments which is any length. `A` is a tuple of arguments type such as `[number, number]` and `B` is the return type such as `number`.

```typescript
const add: Function<[number, number], number> = (a, b) => a+b;
``` 

Futhermore, `Function` which has no type arguments is equal to `(...args: any[])=>any`.


Patch:
diff --git a/LICENSE b/LICENSE
index 1322a741..e58b2fd6 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
 MIT License
 
-Copyright (c) 2017-2019 Giulio Canti
+Copyright (c) 2017-present Giulio Canti
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/docs/_config.yml b/docs/_config.yml
index c184158d..aa83b48c 100644
--- a/docs/_config.yml
+++ b/docs/_config.yml
@@ -6,8 +6,8 @@ search_enabled: true
 # Aux links for the upper right navigation
 aux_links:
   "Docs":
-    - "/"
+    - "//gcanti.github.io/fp-ts/"
   "API Reference":
-    - "/modules/"
+    - "//gcanti.github.io/fp-ts/modules/"
   "GitHub":
     - "//github.com/gcanti/fp-ts"
diff --git a/docs/basics/index.md b/docs/basics/index.md
index ac27e328..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)](../tutorials/debugging-with-Trace.ts)
diff --git a/docs/introduction/core-concepts.md b/docs/introduction/core-concepts.md
index fb05fc6e..d4bbb55e 100644
--- a/docs/introduction/core-concepts.md
+++ b/docs/introduction/core-concepts.md
@@ -18,7 +18,7 @@ We recommend you take the [basic tutorial](../basics/) to get started with using
 
 ## Functions
 
-Functional programming is all about pure functions and how to compose them into bigger structures. `fp-ts` provides a few general [functions](../modules/functions.ts) to support you with composition, currying, and more.
+Functional programming is all about pure functions and how to compose them into bigger structures. `fp-ts` provides a few general [functions](../modules/function.ts) to support you with composition, currying, and more.
 
 ## Data Types
 
diff --git a/docs/introduction/learning-resources.md b/docs/introduction/learning-resources.md
index 057f7396..57ea6867 100644
--- a/docs/introduction/learning-resources.md
+++ b/docs/introduction/learning-resources.md
@@ -32,8 +32,13 @@ If you are coming from Haskell or Purescript:
   - [Setoid](https://dev.to/gcanti/getting-started-with-fp-ts-setoid-39f3)
   - [Ord](https://dev.to/gcanti/getting-started-with-fp-ts-ord-5f1e)
   - [Semigroup](https://dev.to/gcanti/getting-started-with-fp-ts-semigroup-2mf7)
+  - [Monoid](https://dev.to/gcanti/getting-started-with-fp-ts-monoid-ja0)
+  - [Category](https://dev.to/gcanti/getting-started-with-fp-ts-category-4c9a)
+  - [Functor](https://dev.to/gcanti/getting-started-with-fp-ts-functor-36ek)
+  - [Applicative](https://dev.to/gcanti/getting-started-with-fp-ts-applicative-1kb3)
 - "Functional design" series
   - [combinators](https://dev.to/gcanti/functional-design-combinators-14pn)
   - [how to make the `time` combinator more general](https://dev.to/gcanti/functional-design-how-to-make-the-time-combinator-more-general-3fge)
   - [tagless final](https://dev.to/gcanti/functional-design-tagless-final-332k)
   - [smart constructors](https://dev.to/gcanti/functional-design-smart-constructors-14nb)
+  - [Algebraic Data Types](https://dev.to/gcanti/functional-design-algebraic-data-types-36kf)
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/Array.ts.md b/docs/modules/Array.ts.md
index 3480593b..1fb2a418 100644
--- a/docs/modules/Array.ts.md
+++ b/docs/modules/Array.ts.md
@@ -30,9 +30,11 @@ Adapted from https://github.com/purescript/purescript-arrays
 - [elem (function)](#elem-function)
 - [filter (function)](#filter-function)
 - [findFirst (function)](#findfirst-function)
+- [findFirstMap (function)](#findfirstmap-function)
 - [findIndex (function)](#findindex-function)
 - [findLast (function)](#findlast-function)
 - [findLastIndex (function)](#findlastindex-function)
+- [findLastMap (function)](#findlastmap-function)
 - [flatten (function)](#flatten-function)
 - [fold (function)](#fold-function)
 - [foldL (function)](#foldl-function)
@@ -265,12 +267,12 @@ Added in v1.10.0
 
 # cons (function)
 
-Attaches an element to the front of an array, creating a new array
+Attaches an element to the front of an array, creating a new non empty array
 
 **Signature**
 
 ```ts
-export const cons = <A>(a: A, as: Array<A>): Array<A> => ...
+export const cons = <A>(a: A, as: Array<A>): NonEmptyArray<A> => ...
 ```
 
 **Example**
@@ -456,6 +458,35 @@ assert.deepStrictEqual(findFirst([{ a: 1, b: 1 }, { a: 1, b: 2 }], x => x.a ===
 
 Added in v1.0.0
 
+# findFirstMap (function)
+
+Find the first element returned by an option based selector function
+
+**Signature**
+
+```ts
+export const findFirstMap = <A, B>(arr: Array<A>, f: (a: A) => Option<B>): Option<B> => ...
+```
+
+**Example**
+
+```ts
+import { findFirstMap } from 'fp-ts/lib/Array'
+import { some, none } from 'fp-ts/lib/Option'
+
+interface Person {
+  name: string
+  age?: number
+}
+
+const persons: Array<Person> = [{ name: 'John' }, { name: 'Mary', age: 45 }, { name: 'Joey', age: 28 }]
+
+// returns the name of the first person that has an age
+assert.deepStrictEqual(findFirstMap(persons, p => (p.age === undefined ? none : some(p.name))), some('Mary'))
+```
+
+Added in v1.16.0
+
 # findIndex (function)
 
 Find the first index for which a predicate holds
@@ -527,6 +558,35 @@ assert.deepStrictEqual(findLastIndex(xs, x => x.a === 4), none)
 
 Added in v1.10.0
 
+# findLastMap (function)
+
+Find the last element returned by an option based selector function
+
+**Signature**
+
+```ts
+export const findLastMap = <A, B>(arr: Array<A>, f: (a: A) => Option<B>): Option<B> => ...
+```
+
+**Example**
+
+```ts
+import { findLastMap } from 'fp-ts/lib/Array'
+import { some, none } from 'fp-ts/lib/Option'
+
+interface Person {
+  name: string
+  age?: number
+}
+
+const persons: Array<Person> = [{ name: 'John' }, { name: 'Mary', age: 45 }, { name: 'Joey', age: 28 }]
+
+// returns the name of the last person that has an age
+assert.deepStrictEqual(findLastMap(persons, p => (p.age === undefined ? none : some(p.name))), some('Joey'))
+```
+
+Added in v1.16.0
+
 # flatten (function)
 
 Removes one level of nesting
@@ -1139,12 +1199,12 @@ Added in v1.1.0
 
 # snoc (function)
 
-Append an element to the end of an array, creating a new array
+Append an element to the end of an array, creating a new non empty array
 
 **Signature**
 
 ```ts
-export const snoc = <A>(as: Array<A>, a: A): Array<A> => ...
+export const snoc = <A>(as: Array<A>, a: A): NonEmptyArray<A> => ...
 ```
 
 **Example**
diff --git a/docs/modules/Console.ts.md b/docs/modules/Console.ts.md
index cb980c2f..8672db49 100644
--- a/docs/modules/Console.ts.md
+++ b/docs/modules/Console.ts.md
@@ -24,7 +24,7 @@ Adapted from https://github.com/purescript/purescript-console
 **Signature**
 
 ```ts
-export const error = (s: any): IO<void> => ...
+export const error = (s: unknown): IO<void> => ...
 ```
 
 Added in v1.0.0
@@ -34,7 +34,7 @@ Added in v1.0.0
 **Signature**
 
 ```ts
-export const info = (s: any): IO<void> => ...
+export const info = (s: unknown): IO<void> => ...
 ```
 
 Added in v1.0.0
@@ -44,7 +44,7 @@ Added in v1.0.0
 **Signature**
 
 ```ts
-export const log = (s: any): IO<void> => ...
+export const log = (s: unknown): IO<void> => ...
 ```
 
 Added in v1.0.0
@@ -54,7 +54,7 @@ Added in v1.0.0
 **Signature**
 
 ```ts
-export const warn = (s: any): IO<void> => ...
+export const warn = (s: unknown): IO<void> => ...
 ```
 
 Added in v1.0.0
diff --git a/docs/modules/NonEmptyArray2v.ts.md b/docs/modules/NonEmptyArray2v.ts.md
index 424f6866..96de57e6 100644
--- a/docs/modules/NonEmptyArray2v.ts.md
+++ b/docs/modules/NonEmptyArray2v.ts.md
@@ -15,7 +15,9 @@ Data structure which represents non-empty arrays
 - [NonEmptyArray (interface)](#nonemptyarray-interface)
 - [URI (type alias)](#uri-type-alias)
 - [URI (constant)](#uri-constant)
+- [cons (constant)](#cons-constant)
 - [nonEmptyArray (constant)](#nonemptyarray-constant)
+- [snoc (constant)](#snoc-constant)
 - [filter (function)](#filter-function)
 - [filterWithIndex (function)](#filterwithindex-function)
 - [findFirst (function)](#findfirst-function)
@@ -72,6 +74,26 @@ export type URI = typeof URI
 export const URI = ...
 ```
 
+# cons (constant)
+
+Append an element to the front of an array, creating a new non empty array
+
+**Signature**
+
+```ts
+export const cons: <A>(a: A, as: Array<A>) => NonEmptyArray<A> = ...
+```
+
+**Example**
+
+```ts
+import { cons } from 'fp-ts/lib/NonEmptyArray2v'
+
+assert.deepStrictEqual(cons(1, [2, 3, 4]), [1, 2, 3, 4])
+```
+
+Added in v1.16.0
+
 # nonEmptyArray (constant)
 
 **Signature**
@@ -86,6 +108,26 @@ export const nonEmptyArray: Monad1<URI> &
 
 Added in v1.15.0
 
+# snoc (constant)
+
+Append an element to the end of an array, creating a new non empty array
+
+**Signature**
+
+```ts
+export const snoc: <A>(as: Array<A>, a: A) => NonEmptyArray<A> = ...
+```
+
+**Example**
+
+```ts
+import { snoc } from 'fp-ts/lib/NonEmptyArray2v'
+
+assert.deepStrictEqual(snoc([1, 2, 3], 4), [1, 2, 3, 4])
+```
+
+Added in v1.16.0
+
 # filter (function)
 
 **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/Traced.ts.md b/docs/modules/Traced.ts.md
new file mode 100644
index 00000000..7f61235a
--- /dev/null
+++ b/docs/modules/Traced.ts.md
@@ -0,0 +1,127 @@
+---
+title: Traced.ts
+nav_order: 88
+parent: Modules
+---
+
+---
+
+<h2 class="text-delta">Table of contents</h2>
+
+- [URI (type alias)](#uri-type-alias)
+- [Traced (class)](#traced-class)
+  - [map (method)](#map-method)
+- [URI (constant)](#uri-constant)
+- [traced (constant)](#traced-constant)
+- [censor (function)](#censor-function)
+- [getComonad (function)](#getcomonad-function)
+- [listen (function)](#listen-function)
+- [listens (function)](#listens-function)
+- [tracks (function)](#tracks-function)
+
+---
+
+# URI (type alias)
+
+**Signature**
+
+```ts
+export type URI = typeof URI
+```
+
+# Traced (class)
+
+**Signature**
+
+```ts
+export class Traced<P, A> {
+  constructor(readonly run: (p: P) => A) { ... }
+  ...
+}
+```
+
+Added in v1.16.0
+
+## map (method)
+
+**Signature**
+
+```ts
+map<B>(f: (a: A) => B): Traced<P, B> { ... }
+```
+
+# URI (constant)
+
+**Signature**
+
+```ts
+export const URI = ...
+```
+
+# traced (constant)
+
+**Signature**
+
+```ts
+export const traced: Functor2<URI> = ...
+```
+
+Added in v1.16.0
+
+# censor (function)
+
+Apply a function to the current position
+
+**Signature**
+
+```ts
+export const censor = <P, A>(wa: Traced<P, A>, f: (p: P) => P): Traced<P, A> => ...
+```
+
+Added in v1.16.0
+
+# getComonad (function)
+
+**Signature**
+
+```ts
+export function getComonad<P>(monoid: Monoid<P>): Comonad2C<URI, P> { ... }
+```
+
+Added in v1.16.0
+
+# listen (function)
+
+Get the current position
+
+**Signature**
+
+```ts
+export const listen = <P, A>(wa: Traced<P, A>): Traced<P, [A, P]> => ...
+```
+
+Added in v1.16.0
+
+# listens (function)
+
+Get a value which depends on the current position
+
+**Signature**
+
+```ts
+export const listens = <P, A, B>(wa: Traced<P, A>, f: (p: P) => B): Traced<P, [A, B]> => ...
+```
+
+Added in v1.16.0
+
+# tracks (function)
+
+Extracts a value at a relative position which depends on the current value.
+
+**Signature**
+
+```ts
+export const tracks = <P, A>(M: Monoid<P>, f: (a: A) => P) => (wa: Traced<P, A>): A => ...
+```
+
+Added in v1.16.0
diff --git a/docs/modules/Traversable.ts.md b/docs/modules/Traversable.ts.md
index b4bc740c..074dcf56 100644
--- a/docs/modules/Traversable.ts.md
+++ b/docs/modules/Traversable.ts.md
@@ -1,6 +1,6 @@
 ---
 title: Traversable.ts
-nav_order: 88
+nav_order: 89
 parent: Modules
 ---
 
diff --git a/docs/modules/Traversable2v.ts.md b/docs/modules/Traversable2v.ts.md
index 1bdc1534..2ce34ec8 100644
--- a/docs/modules/Traversable2v.ts.md
+++ b/docs/modules/Traversable2v.ts.md
@@ -1,6 +1,6 @@
 ---
 title: Traversable2v.ts
-nav_order: 89
+nav_order: 90
 parent: Modules
 ---
 
diff --git a/docs/modules/TraversableWithIndex.ts.md b/docs/modules/TraversableWithIndex.ts.md
index 4cf80fb3..23adcd59 100644
--- a/docs/modules/TraversableWithIndex.ts.md
+++ b/docs/modules/TraversableWithIndex.ts.md
@@ -1,6 +1,6 @@
 ---
 title: TraversableWithIndex.ts
-nav_order: 90
+nav_order: 91
 parent: Modules
 ---
 
diff --git a/docs/modules/Tree.ts.md b/docs/modules/Tree.ts.md
index cd25ae3e..7f41e624 100644
--- a/docs/modules/Tree.ts.md
+++ b/docs/modules/Tree.ts.md
@@ -1,6 +1,6 @@
 ---
 title: Tree.ts
-nav_order: 91
+nav_order: 92
 parent: Modules
 ---
 
diff --git a/docs/modules/Tuple.ts.md b/docs/modules/Tuple.ts.md
index d0117d66..1d09f022 100644
--- a/docs/modules/Tuple.ts.md
+++ b/docs/modules/Tuple.ts.md
@@ -1,6 +1,6 @@
 ---
 title: Tuple.ts
-nav_order: 92
+nav_order: 93
 parent: Modules
 ---
 
diff --git a/docs/modules/Unfoldable.ts.md b/docs/modules/Unfoldable.ts.md
index 5c5fc0f0..0f807ce4 100644
--- a/docs/modules/Unfoldable.ts.md
+++ b/docs/modules/Unfoldable.ts.md
@@ -1,6 +1,6 @@
 ---
 title: Unfoldable.ts
-nav_order: 93
+nav_order: 94
 parent: Modules
 ---
 
diff --git a/docs/modules/Validation.ts.md b/docs/modules/Validation.ts.md
index ff3e0fbe..ef1366e3 100644
--- a/docs/modules/Validation.ts.md
+++ b/docs/modules/Validation.ts.md
@@ -1,6 +1,6 @@
 ---
 title: Validation.ts
-nav_order: 94
+nav_order: 95
 parent: Modules
 ---
 
@@ -61,6 +61,7 @@ Adapted from https://github.com/purescript/purescript-validation
 - [isFailure (function)](#isfailure-function)
 - [isSuccess (function)](#issuccess-function)
 - [success (function)](#success-function)
+- [tryCatch (function)](#trycatch-function)
 
 ---
 
@@ -557,3 +558,36 @@ export const success = <L, A>(a: A): Validation<L, A> => ...
 ```
 
 Added in v1.0.0
+
+# tryCatch (function)
+
+Constructs a new `Validation` from a function that might throw
+
+**Signature**
+
+```ts
+export const tryCatch = <L, A>(f: Lazy<A>, onError: (e: unknown) => L): Validation<L, A> => ...
+```
+
+**Example**
+
+```ts
+import { Validation, failure, success, tryCatch } from 'fp-ts/lib/Validation'
+
+const unsafeHead = <A>(as: Array<A>): A => {
+  if (as.length > 0) {
+    return as[0]
+  } else {
+    throw new Error('empty array')
+  }
+}
+
+const head = <A>(as: Array<A>): Validation<Error, A> => {
+  return tryCatch(() => unsafeHead(as), e => (e instanceof Error ? e : new Error('unknown error')))
+}
+
+assert.deepStrictEqual(head([]), failure(new Error('empty array')))
+assert.deepStrictEqual(head([1, 2, 3]), success(1))
+```
+
+Added in v1.16.0
diff --git a/docs/modules/Witherable.ts.md b/docs/modules/Witherable.ts.md
index 661b9515..49aa8af4 100644
--- a/docs/modules/Witherable.ts.md
+++ b/docs/modules/Witherable.ts.md
@@ -1,6 +1,6 @@
 ---
 title: Witherable.ts
-nav_order: 95
+nav_order: 96
 parent: Modules
 ---
 
diff --git a/docs/modules/Writer.ts.md b/docs/modules/Writer.ts.md
index 6439aebc..cca2e9a8 100644
--- a/docs/modules/Writer.ts.md
+++ b/docs/modules/Writer.ts.md
@@ -1,6 +1,6 @@
 ---
 title: Writer.ts
-nav_order: 96
+nav_order: 97
 parent: Modules
 ---
 
diff --git a/docs/modules/Zipper.ts.md b/docs/modules/Zipper.ts.md
index 5cb5104d..69508302 100644
--- a/docs/modules/Zipper.ts.md
+++ b/docs/modules/Zipper.ts.md
@@ -1,6 +1,6 @@
 ---
 title: Zipper.ts
-nav_order: 97
+nav_order: 98
 parent: Modules
 ---
 
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/modules/index.ts.md b/docs/modules/index.ts.md
index 6929e82a..bac2213a 100644
--- a/docs/modules/index.ts.md
+++ b/docs/modules/index.ts.md
@@ -94,6 +94,7 @@ parent: Modules
 - [taskEither (export)](#taskeither-export)
 - [these (export)](#these-export)
 - [trace (export)](#trace-export)
+- [traced (export)](#traced-export)
 - [traversable (export)](#traversable-export)
 - [traversable2v (export)](#traversable2v-export)
 - [traversableWithIndex (export)](#traversablewithindex-export)
@@ -795,6 +796,14 @@ export { these }
 export { trace }
 ```
 
+# traced (export)
+
+**Signature**
+
+```ts
+export { traced }
+```
+
 # traversable (export)
 
 **Signature**
diff --git a/docs/recipes/HKT.md b/docs/recipes/HKT.md
index 23d12433..424ce18b 100644
--- a/docs/recipes/HKT.md
+++ b/docs/recipes/HKT.md
@@ -1,7 +1,7 @@
 ---
 title: Write type class instances
 parent: Recipes
-nav_order: 2
+nav_order: 6
 ---
 
 # How to write type class instances for your data type
diff --git a/docs/recipes/async.md b/docs/recipes/async.md
new file mode 100644
index 00000000..3f99b51c
--- /dev/null
+++ b/docs/recipes/async.md
@@ -0,0 +1,77 @@
+---
+title: Async tasks
+parent: Recipes
+nav_order: 4
+---
+
+# Async tasks
+
+## Tasks that always succeed
+
+If you're working with asynchronous tasks that are guaranteed to succeed, use [Task](../modules/Task.ts).
+
+```ts
+import { Task } from 'fp-ts/lib/Task'
+
+const deepThought = new Task<number>(() => Promise.resolve(42))
+
+deepThought.run().then(n => {
+  console.log(`The answer is ${n}.`)
+})
+```
+
+## Tasks that may fail
+
+If you're working with asynchronous tasks that may fail, use [TaskEither](../modules/TaskEither.ts). If the JSON in this example is malformed (try it!), an "I'm sorry" message is displayed.
+
+```ts
+import { tryCatch } from 'fp-ts/lib/TaskEither'
+
+const fetchGreeting = tryCatch<Error, { name: string }>(
+  () => new Promise(resolve => resolve(JSON.parse('{ "name": "Carol" }'))),
+  reason => new Error(String(reason))
+)
+
+fetchGreeting
+  .run()
+  .then(te => te.fold(
+    err => `I'm sorry, I don't know who you are. (${err.message})`,
+    x => `Hello, ${x.name}!`
+  ))
+  .then(console.log)
+```
+
+## Work with a list of tasks in parallel
+
+JavaScript provides `Promises.all` to wait for a list of Promises.
+
+```ts
+Promise.all([Promise.resolve(1), Promise.resolve(2)]).then(console.log) // [1, 2]
+```
+
+With `Task`s you can achieve the same using `sequence`. Both the `Promise.all` and the `sequence` approach run in parallel and wait until all results have arrived before they proceed.
+
+```ts
+import { task } from 'fp-ts/lib/Task'
+import { array } from 'fp-ts/lib/Array'
+
+const tasks = [task.of(1), task.of(2)]
+array
+  .sequence(task)(tasks)
+  .run()
+  .then(console.log) // [ 1, 2 ]
+```
+
+## Work with a list of dependent tasks
+
+If you need the result of on task before you can continue with the next, you can `chain` the tasks like so:
+
+```ts
+import { task } from 'fp-ts/lib/Task'
+
+task.of(2)
+  .chain(result => task.of(result * 3))
+  .chain(result => task.of(result + 4))
+  .run()
+  .then(console.log) // 10
+```
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/docs/recipes/equality.md b/docs/recipes/equality.md
new file mode 100644
index 00000000..906a28ae
--- /dev/null
+++ b/docs/recipes/equality.md
@@ -0,0 +1,141 @@
+---
+title: Equality
+parent: Recipes
+nav_order: 1
+---
+
+# How to determine if two things are equal
+
+With `fp-ts` you can test whether two values are equal using a `Setoid`. You can also compose equality functions to test deep structures and create your own definitions of equality.
+
+We show the most common usages here, but if you need more ways to check for equality, be sure to read the [Setoid](../modules/Setoid.ts) documentation page.
+
+## Primitive equality
+
+```ts
+import { setoidBoolean,  setoidDate, setoidNumber, setoidString } from 'fp-ts/lib/Setoid'
+
+setoidBoolean.equals(true, true) // true
+setoidDate.equals(new Date('1984-01-27'), new Date('1984-01-27')) // true
+setoidNumber.equals(3, 3) // true
+setoidString.equals('Cyndi', 'Cyndi') // true
+```
+
+## Compare structures
+
+```ts
+import { Setoid, getStructSetoid, setoidNumber } from 'fp-ts/lib/Setoid'
+
+type Point = {
+  x: number
+  y: number
+}
+
+const setoidPoint: Setoid<Point> = getStructSetoid({
+  x: setoidNumber,
+  y: setoidNumber
+})
+
+setoidPoint.equals({ x: 0, y: 0 }, { x: 0, y: 0 }) // true
+```
+
+This structure can be combined further:
+
+```ts
+type Vector = {
+  from: Point
+  to: Point
+}
+
+const setoidVector: Setoid<Vector> = getStructSetoid({
+  from: setoidPoint,
+  to: setoidPoint
+})
+
+setoidVector.equals(
+  { from: { x: 0, y: 0 }, to: { x: 0, y: 0 } },
+  { from: { x: 0, y: 0 }, to: { x: 0, y: 0 } }
+) // true
+```
+
+## Compare arrays
+
+```ts
+import { Setoid, getArraySetoid, getStructSetoid, setoidString } from 'fp-ts/lib/Setoid'
+
+const setoidArrayOfStrings = getArraySetoid(setoidString)
+
+setoidArrayOfStrings.equals(
+  ['Time', 'After', 'Time'],
+  ['Time', 'After', 'Time']
+) // true
+```
+
+Test the equality of structures nested within arrays:
+
+```ts
+import { Setoid, getArraySetoid, getStructSetoid, setoidNumber } from 'fp-ts/lib/Setoid'
+
+type Point = {
+  x: number
+  y: number
+}
+
+const setoidPoint: Setoid<Point> = getStructSetoid({
+  x: setoidNumber,
+  y: setoidNumber
+})
+
+const setoidArrayOfPoints: Setoid<Array<Point>> = getArraySetoid(setoidPoint)
+
+setoidArrayOfPoints.equals(
+  [{ x: 0, y: 0 }, { x: 4, y: 0 }],
+  [{ x: 0, y: 0 }, { x: 4, y: 0 }]
+) // true
+```
+
+## Custom definitions
+
+In this example, two users are equal if their `userId` field is equal.
+
+```ts
+import { contramap, setoidNumber } from 'fp-ts/lib/Setoid'
+
+type User = {
+  userId: number
+  name: string
+}
+
+const setoidUserId = contramap((user: User) => user.userId, setoidNumber)
+
+setoidUserId.equals({ userId: 1, name: 'Giulio' }, { userId: 1, name: 'Giulio Canti' }) // true
+setoidUserId.equals({ userId: 1, name: 'Giulio' }, { userId: 2, name: 'Giulio' }) // false
+```
+
+## More Setoid instances
+
+Many data types provide `Setoid` instances. Here's [Option](../modules/Option.ts):
+
+```ts
+import { getSetoid, none, some } from 'fp-ts/lib/Option'
+import { setoidNumber } from 'fp-ts/lib/Setoid'
+
+const O = getSetoid(setoidNumber)
+
+O.equals(some(3), some(3)) // true
+O.equals(none, some(4)) // false
+O.equals(none, none) // true
+```
+
+It works similarly for [Either](../modules/Either.ts) and other types where it is possible to determine equality:
+
+```ts
+import { getSetoid, left, right } from 'fp-ts/lib/Either'
+import { setoidNumber, setoidString } from 'fp-ts/lib/Setoid'
+
+const O = getSetoid(setoidString, setoidNumber)
+
+O.equals(right(3), right(3)) // true
+O.equals(left('3'), right(3)) // false
+O.equals(left('3'), left('3')) // true
+```
diff --git a/docs/recipes/interoperability.md b/docs/recipes/interoperability.md
index 7cb5e30d..4dfb68e2 100644
--- a/docs/recipes/interoperability.md
+++ b/docs/recipes/interoperability.md
@@ -1,7 +1,7 @@
 ---
 title: Work with non-functional code
 parent: Recipes
-nav_order: 1
+nav_order: 5
 ---
 
 # How to work with non-functional code using fp-ts
@@ -13,7 +13,7 @@ Sometimes you are forced to interoperate with code not written in a functional s
 <dl>
   <dt>Use case</dt><dd>an API that may fail and returns a special value of the codomain.</dd>
   <dt>Example</dt><dd><code>Array.prototype.findIndex</code></dd>
-  <dt>Solution</dt><dd><a href="../modules/Option.ts.html">Option</a></dd>
+  <dt>Solution</dt><dd><a href="../modules/Option.ts">Option</a></dd>
 </dl>
 
 ```ts
@@ -30,7 +30,7 @@ function findIndex<A>(as: Array<A>, predicate: (a: A) => boolean): Option<number
 <dl>
   <dt>Use case</dt><dd>an API that may fail and returns undefined (or null).</dd>
   <dt>Example</dt><dd><code>Array.prototype.find</code></dd>
-  <dt>Solution</dt><dd><a href="../modules/Option.ts.html">Option</a>, <a href="../modules/Option.ts.html#fromnullable-function">fromNullable</a></dd>
+  <dt>Solution</dt><dd><a href="../modules/Option.ts">Option</a>, <a href="../modules/Option.ts#fromnullable-function">fromNullable</a></dd>
 </dl>
 
 ```ts
@@ -46,7 +46,7 @@ function find<A>(as: Array<A>, predicate: (a: A) => boolean): Option<A> {
 <dl>
   <dt>Use case</dt><dd>an API that may throw.</dd>
   <dt>Example</dt><dd><code>JSON.parse</code></dd>
-  <dt>Solution</dt><dd><a href="../modules/Either.ts.html">Either</a>, <a href="../modules/Either.ts.html#trycatch2v-function">tryCatch2v</a></dd>
+  <dt>Solution</dt><dd><a href="../modules/Either.ts">Either</a>, <a href="../modules/Either.ts#trycatch2v-function">tryCatch2v</a></dd>
 </dl>
 
 ```ts
@@ -62,7 +62,7 @@ function parse(s: string): Either<Error, unknown> {
 <dl>
   <dt>Use case</dt><dd>an API that returns a non deterministic value.</dd>
   <dt>Example</dt><dd><code>Math.random</code></dd>
-  <dt>Solution</dt><dd><a href="../modules/IO.ts.html">IO</a></dd>
+  <dt>Solution</dt><dd><a href="../modules/IO.ts">IO</a></dd>
 </dl>
 
 ```ts
@@ -76,7 +76,7 @@ const random: IO<number> = new IO(() => Math.random())
 <dl>
   <dt>Use case</dt><dd>an API that reads and/or writes to a global state.</dd>
   <dt>Example</dt><dd><code>localStorage.getItem</code></dd>
-  <dt>Solution</dt><dd><a href="../modules/IO.ts.html">IO</a></dd>
+  <dt>Solution</dt><dd><a href="../modules/IO.ts">IO</a></dd>
 </dl>
 
 ```ts
@@ -91,7 +91,7 @@ function getItem(key: string): IO<Option<string>> {
 <dl>
   <dt>Use case</dt><dd>an API that reads and/or writes to a global state and may throw.</dd>
   <dt>Example</dt><dd><code>readFileSync</code></dd>
-  <dt>Solution</dt><dd><a href="../modules/IOEither.ts.html">IOEither</a>, <a href="../modules/IOEither.ts.html#trycatch2v-function">tryCatch2v</a></dd>
+  <dt>Solution</dt><dd><a href="../modules/IOEither.ts">IOEither</a>, <a href="../modules/IOEither.ts#trycatch2v-function">tryCatch2v</a></dd>
 </dl>
 
 ```ts
@@ -108,7 +108,7 @@ function readFileSync(path: string): IOEither<Error, string> {
 <dl>
   <dt>Use case</dt><dd>an API that performs an asynchronous computation.</dd>
   <dt>Example</dt><dd>reading from standard input</dd>
-  <dt>Solution</dt><dd><a href="../modules/Task.ts.html">Task</a></dd>
+  <dt>Solution</dt><dd><a href="../modules/Task.ts">Task</a></dd>
 </dl>
 
 ```ts
@@ -133,7 +133,7 @@ const read: Task<string> = new Task(
 <dl>
   <dt>Use case</dt><dd>an API that performs an asynchronous computation and may reject.</dd>
   <dt>Example</dt><dd>fetch</dd>
-  <dt>Solution</dt><dd><a href="modules/TaskEither.ts.html">TaskEither</a>, <a href="modules/TaskEither.ts.html#trycatch-function">tryCatch</a></dd>
+  <dt>Solution</dt><dd><a href="../modules/TaskEither.ts">TaskEither</a>, <a href="../modules/TaskEither.ts#trycatch-function">tryCatch</a></dd>
 </dl>
 
 ```ts
diff --git a/docs/recipes/numbers.md b/docs/recipes/numbers.md
new file mode 100644
index 00000000..6f7a5636
--- /dev/null
+++ b/docs/recipes/numbers.md
@@ -0,0 +1,106 @@
+---
+title: Numbers
+parent: Recipes
+nav_order: 3
+---
+
+# Working with numbers
+
+`fp-ts` is not a math library, but there are some good facilities we can use to work with numbers. Because the examples below use abstract concepts, e.g. [Monoid](../modules/Monoid.ts)s, many of the examples below would work with other types of data, not just numbers.
+
+## Min/max
+
+```ts
+import { boundedNumber } from 'fp-ts/lib/Bounded'
+import { fold, getJoinMonoid, getMeetMonoid } from 'fp-ts/lib/Monoid'
+
+const min = fold(getMeetMonoid(boundedNumber))
+const max = fold(getJoinMonoid(boundedNumber))
+
+min([5, 2, 3]) // 2
+max([5, 2, 3]) // 5
+```
+
+## Sums and products
+
+```ts
+import { fold, monoidProduct, monoidSum } from 'fp-ts/lib/Monoid'
+
+const sum = fold(monoidSum)
+const product = fold(monoidProduct)
+
+sum([1, 2, 3, 4]) // 10
+product([1, 2, 3, 4]) // 24
+```
+
+## Working with nested structures
+
+```ts
+import { getStructMonoid, Monoid, monoidSum } from 'fp-ts/lib/Monoid'
+
+type Point = {
+  x: number
+  y: number
+}
+
+const monoidPoint: Monoid<Point> = getStructMonoid({
+  x: monoidSum,
+  y: monoidSum
+})
+
+monoidPoint.concat({ x: 0, y: 3 }, { x: 2, y: 4 }) // { x: 2, y: 7 }
+```
+
+To check whether the resulting `Point` is positive, create a predicate:
+
+```ts
+import { getFunctionMonoid, Monoid, monoidAll } from 'fp-ts/lib/Monoid'
+
+type Point = {
+  x: number
+  y: number
+}
+
+const monoidPredicate: Monoid<(p: Point) => boolean> = getFunctionMonoid(monoidAll)<Point>()
+
+const isPositiveX = (p: Point): boolean => p.x >= 0
+const isPositiveY = (p: Point): boolean => p.y >= 0
+
+const isPositiveXY = monoidPredicate.concat(isPositiveX, isPositiveY)
+
+isPositiveXY({ x: 1, y: 1 }) // true
+isPositiveXY({ x: 1, y: -1 }) // false
+isPositiveXY({ x: -1, y: 1 }) // false
+isPositiveXY({ x: -1, y: -1 }) // false
+```
+
+## Working with optional values
+
+```ts
+import { fold, monoidProduct, monoidSum } from 'fp-ts/lib/Monoid'
+import { getApplyMonoid, none, some } from 'fp-ts/lib/Option'
+
+const sum = fold(getApplyMonoid(monoidSum))
+const product = fold(getApplyMonoid(monoidProduct))
+
+sum([some(2), none, some(4)]) // none
+sum([some(2), some(3), some(4)]) // some(9)
+
+product([some(2), none, some(4)]) // none
+product([some(2), some(3), some(4)]) // some(24)
+```
+
+This also works for [Either](../modules/Either.ts)s, but note that folding on `Left` values does not work the same way as folding on `Right` values.
+
+```ts
+import { getApplyMonoid, left, right } from 'fp-ts/lib/Either'
+import { fold, monoidProduct, monoidSum } from 'fp-ts/lib/Monoid'
+
+const sum = fold(getApplyMonoid(monoidSum))
+const product = fold(getApplyMonoid(monoidProduct))
+
+sum([right(2), left(3), right(4)]) // left(3)
+sum([right(2), right(3), right(4)]) // right(9)
+product([right(2), left(3), left(4)]) // left(3) <- it's the first left value
+product([right(2), right(3), right(4)]) // right(24)
+```
diff --git a/docs/recipes/ordering.md b/docs/recipes/ordering.md
new file mode 100644
index 00000000..8c98e20c
--- /dev/null
+++ b/docs/recipes/ordering.md
@@ -0,0 +1,156 @@
+---
+title: Ordering
+parent: Recipes
+nav_order: 2
+---
+
+# How to determine the order of data
+
+If you need to decide on the order of two values, you can make use of the `compare` method provided by `Ord` instances. Ordering builds on [equality](./equality).
+
+Note that `compare` returns an [Ordering](../modules/Ordering.ts), which is one of these values `-1 | 0 | 1`. We say that
+
+* `x < y` if and only if `compare(x, y)` is equal to `-1`
+* `x` is equal to `y` if and only if `compare(x, y)` is equal to `0`
+* `x > y` if and only if `compare(x, y)` is equal to `1`
+
+We show the most common usages here, but if you need more ways to order your data, be sure to read the [Ord](../modules/Ord.ts) documentation page.
+
+## Primitive comparisons
+
+```ts
+import { ordBoolean, ordDate, ordNumber, ordString } from 'fp-ts/lib/Ord'
+
+ordNumber.compare(4, 5) // -1
+ordNumber.compare(5, 5) // 0
+ordNumber.compare(6, 5) // 1
+
+ordBoolean.compare(true, false) // 1
+ordDate.compare(new Date('1984-01-27'), new Date('1978-09-23')) // 1
+ordString.compare('Cyndi', 'Debbie') // -1
+```
+
+Note that all `Ord` instances also define the `equals` method, because it is a prerequisite to be able to compare data.
+
+```ts
+ordBoolean.equals(false, false) // true
+```
+
+## Custom comparisons
+
+You can create custom comparisons using `fromCompare` like so:
+
+```ts
+import { fromCompare } from 'fp-ts/lib/Ord'
+
+const strlenOrd = fromCompare((a: string, b: string) => (a.length < b.length ? -1 : a.length > b.length ? 1 : 0))
+strlenOrd.compare('Hi', 'there') // -1
+strlenOrd.compare('Goodbye', 'friend') // 1
+```
+
+But most of the time, you can achieve the same result in a simpler way with `contramap`:
+
+```ts
+import { contramap, ordNumber } from 'fp-ts/lib/Ord'
+
+const strlenOrd = contramap((s: string) => s.length, ordNumber)
+strlenOrd.compare('Hi', 'there') // -1
+strlenOrd.compare('Goodbye', 'friend') // 1
+```
+
+## Min, max, clamp
+
+Take the smaller (`min`) or larger (`max`) element of two, or take the one closest to the given boundaries (`clamp`).
+
+```ts
+import { clamp, max, min, ordNumber, ordString } from 'fp-ts/lib/Ord'
+
+min(ordNumber)(5, 2) // 2
+max(ordNumber)(5, 2) // 5
+
+clamp(ordNumber)(3, 7)(2) // 3
+clamp(ordString)('Bar', 'Boat')('Ball') // Bar
+```
+
+## Less than, greater than, or in between?
+
+```ts
+import { between, greaterThanOrEq, lessThan, ordNumber } from 'fp-ts/lib/Ord'
+
+lessThan(ordNumber)(4, 7) // true
+greaterThanOrEq(ordNumber)(6, 6) // true
+
+between(ordNumber)(6, 9)(7) // true
+between(ordNumber)(6, 9)(6) // true
+between(ordNumber)(6, 9)(9) // true
+between(ordNumber)(6, 9)(12) // false
+```
+
+
+## Sort an array
+
+```ts
+import { ordNumber } from 'fp-ts/lib/Ord'
+import { sort } from 'fp-ts/lib/Array'
+
+const sortByNumber = sort(ordNumber)
+sortByNumber([3,1,2]) // [1, 2, 3]
+```
+
+Sort an array of objects:
+
+```ts
+import { contramap, ordNumber } from 'fp-ts/lib/Ord'
+import { sort } from 'fp-ts/lib/Array'
+
+type Planet = {
+  name: string
+  diameter: number // km
+  distance: number // AU from Sun
+}
+
+const planets: Array<Planet> = [
+  { name: 'Earth', diameter: 12756, distance: 1 },
+  { name: 'Jupiter', diameter: 142800, distance: 5.203 },
+  { name: 'Mars', diameter: 6779, distance: 1.524 },
+  { name: 'Mercury', diameter: 4879.4, distance: 0.39 },
+  { name: 'Neptune', diameter: 49528, distance: 30.06 },
+  { name: 'Saturn', diameter: 120660, distance: 9.539 },
+  { name: 'Uranus', diameter: 51118, distance: 19.18 },
+  { name: 'Venus', diameter: 12104, distance: 0.723 }
+]
+
+const diameterOrd = contramap((x: Planet) => x.diameter, ordNumber)
+const distanceOrd = contramap((x: Planet) => x.distance, ordNumber)
+
+console.log(sort(distanceOrd)(planets)) // Mercury, Venus, Earth, Mars, ...
+console.log(sort(diameterOrd)(planets)) // Mercury, Mars, Venus, Earth, ...
+```
+
+## More Ord instances
+
+Many data types provide `Ord` instances. Here's [Option](../modules/Option.ts):
+
+```ts
+import { getOrd, none, some } from 'fp-ts/lib/Option'
+import { ordNumber } from 'fp-ts/lib/Ord'
+
+const O = getOrd(ordNumber)
+O.compare(none, none) // 0
+O.compare(none, some(1)) // -1
+O.compare(some(1), none) // 1
+O.compare(some(1), some(2)) // -1
+O.compare(some(1), some(1)) // 0
+```
+
+It works similarly for [Tuple](../modules/Tuple.ts)s and other types where it is possible to determine order:
+
+```ts
+import { ordNumber, ordString } from 'fp-ts/lib/Ord'
+import { getOrd, Tuple } from 'fp-ts/lib/Tuple'
+
+const O = getOrd(ordString, ordNumber)
+O.compare(new Tuple('A', 10), new Tuple('A', 12)) // -1
+O.compare(new Tuple('A', 10), new Tuple('A', 4)) // 1
+O.compare(new Tuple('A', 10), new Tuple('B', 4)) // -1
+```
diff --git a/docs/recipes/purescript.md b/docs/recipes/purescript.md
index 91d062e7..f572d25f 100644
--- a/docs/recipes/purescript.md
+++ b/docs/recipes/purescript.md
@@ -1,7 +1,7 @@
 ---
 title: Migrate from PureScript/Haskell
 parent: Recipes
-nav_order: 4
+nav_order: 8
 ---
 
 # Migrate from PureScript/Haskell
diff --git a/docs/recipes/ramda.md b/docs/recipes/ramda.md
index e40db7a4..cc44b9b5 100644
--- a/docs/recipes/ramda.md
+++ b/docs/recipes/ramda.md
@@ -1,7 +1,7 @@
 ---
 title: Migrate from Ramda
 parent: Recipes
-nav_order: 3
+nav_order: 7
 ---
 
 # Migrate from Ramda
diff --git a/dtslint/ts3.1/index.ts b/dtslint/ts3.1/index.ts
index 4e309676..42dbfb09 100644
--- a/dtslint/ts3.1/index.ts
+++ b/dtslint/ts3.1/index.ts
@@ -1,31 +1,31 @@
-import * as Apv from '../../src/Applicative'
-import * as Apy from '../../src/Apply'
-import * as A from '../../src/Array'
-import * as C from '../../src/Const'
-import * as E from '../../src/Either'
-import * as F from '../../src/Functor'
-import * as H from '../../src/HKT'
-import * as Ix from '../../src/IxIO'
-import * as O from '../../src/Option'
-import * as OT from '../../src/OptionT'
-import * as Re from '../../src/Reader'
-import * as RTE from '../../src/ReaderTaskEither'
-import * as R from '../../src/Record'
-import * as S from '../../src/Semigroup'
-import * as T from '../../src/Task'
-import * as TE from '../../src/TaskEither'
-import * as Th from '../../src/These'
-import * as Tr from '../../src/Traversable'
-import * as U from '../../src/Unfoldable'
-import * as V from '../../src/Validation'
-import * as Mon from '../../src/Monoid'
-import * as Se from '../../src/Setoid'
-import * as SM from '../../src/StrMap'
-import * as Fo from '../../src/Foldable'
-import * as Or from '../../src/Ord'
-import * as Fu from '../../src/function'
-import * as Ring from '../../src/Ring'
-import * as Field from '../../src/Field'
+import * as Apv from '../../lib/Applicative'
+import * as Apy from '../../lib/Apply'
+import * as A from '../../lib/Array'
+import * as C from '../../lib/Const'
+import * as E from '../../lib/Either'
+import * as F from '../../lib/Functor'
+import * as H from '../../lib/HKT'
+import * as Ix from '../../lib/IxIO'
+import * as O from '../../lib/Option'
+import * as OT from '../../lib/OptionT'
+import * as Re from '../../lib/Reader'
+import * as RTE from '../../lib/ReaderTaskEither'
+import * as R from '../../lib/Record'
+import * as S from '../../lib/Semigroup'
+import * as T from '../../lib/Task'
+import * as TE from '../../lib/TaskEither'
+import * as Th from '../../lib/These'
+import * as Tr from '../../lib/Traversable'
+import * as U from '../../lib/Unfoldable'
+import * as V from '../../lib/Validation'
+import * as Mon from '../../lib/Monoid'
+import * as Se from '../../lib/Setoid'
+import * as SM from '../../lib/StrMap'
+import * as Fo from '../../lib/Foldable'
+import * as Or from '../../lib/Ord'
+import * as Fu from '../../lib/function'
+import * as Ring from '../../lib/Ring'
+import * as Field from '../../lib/Field'
 
 const double = (n: number): number => n * 2
 const len = (s: string): number => s.length
@@ -335,7 +335,7 @@ const toUnfoldable3 = SM.toUnfoldable(A.array)(new SM.StrMap({ a: 1 })) // $Expe
 // NonEmptyArray2v
 //
 
-import * as NEA2v from '../../src/NonEmptyArray2v'
+import * as NEA2v from '../../lib/NonEmptyArray2v'
 
 declare const nea2v1: NEA2v.NonEmptyArray<string>
 declare const nea2v2: NEA2v.NonEmptyArray<string>
diff --git a/dtslint/ts3.1/tslint.json b/dtslint/ts3.1/tslint.json
index 9ce37507..b2f218b1 100644
--- a/dtslint/ts3.1/tslint.json
+++ b/dtslint/ts3.1/tslint.json
@@ -14,6 +14,7 @@
     "object-literal-shorthand": false,
     "prefer-object-spread": false,
     "whitespace": false,
-    "use-default-type-parameter": false
+    "use-default-type-parameter": false,
+    "no-relative-import-in-test": false
   }
 }
diff --git a/package-lock.json b/package-lock.json
index 591ed373..b2395ddf 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
 {
   "name": "fp-ts",
-  "version": "1.14.4",
+  "version": "1.15.0",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {
@@ -1736,7 +1736,7 @@
       }
     },
     "dtslint": {
-      "version": "github:gcanti/dtslint#02aec0b72ae639458f02349c5c68dd6229287a6d",
+      "version": "github:gcanti/dtslint#217abe9ec59cf4323d4e47c2d458312b12eb4983",
       "from": "github:gcanti/dtslint",
       "dev": true,
       "requires": {
@@ -1744,7 +1744,7 @@
         "parsimmon": "^1.12.0",
         "strip-json-comments": "^2.0.1",
         "tslint": "^5.12.0",
-        "typescript": "^3.4.0-dev.20190216"
+        "typescript": "^3.4.0-dev.20190329"
       },
       "dependencies": {
         "ansi-styles": {
@@ -1767,20 +1767,6 @@
             "supports-color": "^5.3.0"
           }
         },
-        "glob": {
-          "version": "7.1.3",
-          "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
-          "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
-          "dev": true,
-          "requires": {
-            "fs.realpath": "^1.0.0",
-            "inflight": "^1.0.4",
-            "inherits": "2",
-            "minimatch": "^3.0.4",
-            "once": "^1.3.0",
-            "path-is-absolute": "^1.0.0"
-          }
-        },
         "has-flag": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
@@ -1797,9 +1783,9 @@
           }
         },
         "tslint": {
-          "version": "5.12.1",
-          "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.12.1.tgz",
-          "integrity": "sha512-sfodBHOucFg6egff8d1BvuofoOQ/nOeYNfbp7LDlKBcLNrL3lmS5zoiDGyOMdT7YsEXAwWpTdAHwOGOc8eRZAw==",
+          "version": "5.14.0",
+          "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.14.0.tgz",
+          "integrity": "sha512-IUla/ieHVnB8Le7LdQFRGlVJid2T/gaJe5VkjzRVSRR6pA2ODYrnfR1hmxi+5+au9l50jBwpbBL34txgv4NnTQ==",
           "dev": true,
           "requires": {
             "babel-code-frame": "^6.22.0",
@@ -1810,10 +1796,11 @@
             "glob": "^7.1.1",
             "js-yaml": "^3.7.0",
             "minimatch": "^3.0.4",
+            "mkdirp": "^0.5.1",
             "resolve": "^1.3.2",
             "semver": "^5.3.0",
             "tslib": "^1.8.0",
-            "tsutils": "^2.27.2"
+            "tsutils": "^2.29.0"
           }
         },
         "tsutils": {
@@ -1826,9 +1813,9 @@
           }
         },
         "typescript": {
-          "version": "3.4.0-dev.20190216",
-          "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.4.0-dev.20190216.tgz",
-          "integrity": "sha512-46/6VqUDONOFriBe/u7aSPon6xF0sBsRWCkE9Ue/vF8ssrligDaW9Cs5YLQ5gv6rgsHJ8IX0HwugcH6hTpCReg==",
+          "version": "3.4.0-dev.20190329",
+          "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.4.0-dev.20190329.tgz",
+          "integrity": "sha512-iEJfMDxQlatDr6WQF5ywln+e5d4yfKSYxUW4brcrS5B+8AgSwdLbRn0VsYYcpG6AyFO0jUeAnuZVnpGvZgJtmw==",
           "dev": true
         }
       }
@@ -7020,9 +7007,9 @@
       "dev": true
     },
     "typescript": {
-      "version": "3.3.3",
-      "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.3.3.tgz",
-      "integrity": "sha512-Y21Xqe54TBVp+VDSNbuDYdGw0BpoR/Q6wo/+35M8PAU0vipahnyduJWirxxdxjsAkS7hue53x2zp8gz7F05u0A==",
+      "version": "3.4.1",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.4.1.tgz",
+      "integrity": "sha512-3NSMb2VzDQm8oBTLH6Nj55VVtUEpe/rgkIzMir0qVoLyjDZlnMBva0U6vDiV3IH+sl/Yu6oP5QwsAQtHPmDd2Q==",
       "dev": true
     },
     "uglify-js": {
diff --git a/package.json b/package.json
index 8671ae89..68d97766 100644
--- a/package.json
+++ b/package.json
@@ -19,7 +19,7 @@
     "prepublish": "npm run build",
     "doctoc": "doctoc README.md docs/introduction/code-conventions.md --title \"**Table of contents**\"",
     "mocha": "mocha -r ts-node/register test/*.ts",
-    "dtslint": "dtslint dtslint",
+    "dtslint": "npm run build && dtslint dtslint",
     "docs": "docs-ts"
   },
   "repository": {
@@ -56,7 +56,7 @@
     "ts-simple-ast": "17.1.1",
     "tslint": "5.11.0",
     "tslint-config-standard": "8.0.1",
-    "typescript": "^3.3.3"
+    "typescript": "^3.4.1"
   },
   "tags": [
     "typescript",
diff --git a/src/Apply.ts b/src/Apply.ts
index 7a15c006..b4f73886 100644
--- a/src/Apply.ts
+++ b/src/Apply.ts
@@ -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/Array.ts b/src/Array.ts
index e26df277..1eb83511 100644
--- a/src/Array.ts
+++ b/src/Array.ts
@@ -21,6 +21,7 @@ import { getArraySetoid, Setoid } from './Setoid'
 import { TraversableWithIndex1 } from './TraversableWithIndex'
 import { Unfoldable1 } from './Unfoldable'
 import { Witherable1 } from './Witherable'
+import { NonEmptyArray } from './NonEmptyArray2v'
 
 declare global {
   interface Array<T> {
@@ -463,7 +464,7 @@ export const index = <A>(i: number, as: Array<A>): Option<A> => {
 }
 
 /**
- * Attaches an element to the front of an array, creating a new array
+ * Attaches an element to the front of an array, creating a new non empty array
  *
  * @example
  * import { cons } from 'fp-ts/lib/Array'
@@ -472,18 +473,18 @@ export const index = <A>(i: number, as: Array<A>): Option<A> => {
  *
  * @since 1.0.0
  */
-export const cons = <A>(a: A, as: Array<A>): Array<A> => {
+export const cons = <A>(a: A, as: Array<A>): NonEmptyArray<A> => {
   const len = as.length
   const r = Array(len + 1)
   for (let i = 0; i < len; i++) {
     r[i + 1] = as[i]
   }
   r[0] = a
-  return r
+  return r as NonEmptyArray<A>
 }
 
 /**
- * Append an element to the end of an array, creating a new array
+ * Append an element to the end of an array, creating a new non empty array
  *
  * @example
  * import { snoc } from 'fp-ts/lib/Array'
@@ -492,14 +493,14 @@ export const cons = <A>(a: A, as: Array<A>): Array<A> => {
  *
  * @since 1.0.0
  */
-export const snoc = <A>(as: Array<A>, a: A): Array<A> => {
+export const snoc = <A>(as: Array<A>, a: A): NonEmptyArray<A> => {
   const len = as.length
   const r = Array(len + 1)
   for (let i = 0; i < len; i++) {
     r[i] = as[i]
   }
   r[len] = a
-  return r
+  return r as NonEmptyArray<A>
 }
 
 /**
@@ -752,6 +753,36 @@ export function findFirst<A>(as: Array<A>, predicate: Predicate<A>): Option<A> {
   return none
 }
 
+/**
+ * Find the first element returned by an option based selector function
+ *
+ * @example
+ * import { findFirstMap } from 'fp-ts/lib/Array'
+ * import { some, none } from 'fp-ts/lib/Option'
+ *
+ * interface Person {
+ *   name: string
+ *   age?: number
+ * }
+ *
+ * const persons: Array<Person> = [{ name: 'John' }, { name: 'Mary', age: 45 }, { name: 'Joey', age: 28 }]
+ *
+ * // returns the name of the first person that has an age
+ * assert.deepStrictEqual(findFirstMap(persons, p => (p.age === undefined ? none : some(p.name))), some('Mary'))
+ *
+ * @since 1.16.0
+ */
+export const findFirstMap = <A, B>(arr: Array<A>, f: (a: A) => Option<B>): Option<B> => {
+  const len = arr.length
+  for (let i = 0; i < len; i++) {
+    const v = f(arr[i])
+    if (v.isSome()) {
+      return v
+    }
+  }
+  return none
+}
+
 /**
  * Find the last element which satisfies a predicate function
  *
@@ -775,6 +806,36 @@ export function findLast<A>(as: Array<A>, predicate: Predicate<A>): Option<A> {
   return none
 }
 
+/**
+ * Find the last element returned by an option based selector function
+ *
+ * @example
+ * import { findLastMap } from 'fp-ts/lib/Array'
+ * import { some, none } from 'fp-ts/lib/Option'
+ *
+ * interface Person {
+ *   name: string
+ *   age?: number
+ * }
+ *
+ * const persons: Array<Person> = [{ name: 'John' }, { name: 'Mary', age: 45 }, { name: 'Joey', age: 28 }]
+ *
+ * // returns the name of the last person that has an age
+ * assert.deepStrictEqual(findLastMap(persons, p => (p.age === undefined ? none : some(p.name))), some('Joey'))
+ *
+ * @since 1.16.0
+ */
+export const findLastMap = <A, B>(arr: Array<A>, f: (a: A) => Option<B>): Option<B> => {
+  const len = arr.length
+  for (let i = len - 1; i >= 0; i--) {
+    const v = f(arr[i])
+    if (v.isSome()) {
+      return v
+    }
+  }
+  return none
+}
+
 /**
  * Returns the index of the last element of the list which matches the predicate
  *
diff --git a/src/Console.ts b/src/Console.ts
index 25d945bb..51ffe06c 100644
--- a/src/Console.ts
+++ b/src/Console.ts
@@ -6,27 +6,27 @@ import { IO } from './IO'
 /**
  * @since 1.0.0
  */
-export const log = (s: any): IO<void> => {
+export const log = (s: unknown): IO<void> => {
   return new IO(() => console.log(s)) // tslint:disable-line:no-console
 }
 
 /**
  * @since 1.0.0
  */
-export const warn = (s: any): IO<void> => {
+export const warn = (s: unknown): IO<void> => {
   return new IO(() => console.warn(s)) // tslint:disable-line:no-console
 }
 
 /**
  * @since 1.0.0
  */
-export const error = (s: any): IO<void> => {
+export const error = (s: unknown): IO<void> => {
   return new IO(() => console.error(s)) // tslint:disable-line:no-console
 }
 
 /**
  * @since 1.0.0
  */
-export const info = (s: any): IO<void> => {
+export const info = (s: unknown): IO<void> => {
   return new IO(() => console.info(s)) // tslint:disable-line:no-console
 }
diff --git a/src/EitherT.ts b/src/EitherT.ts
index e10107e9..54b630e6 100644
--- a/src/EitherT.ts
+++ b/src/EitherT.ts
@@ -133,7 +133,7 @@ export function right<F extends URIS>(F: Functor1<F>): <L, A>(fa: Type<F, A>) =>
 export function right<F>(F: Functor<F>): <L, A>(fa: HKT<F, A>) => HKT<F, Either<L, A>>
 /** @deprecated */
 export function right<F>(F: Functor<F>): <L, A>(fa: HKT<F, A>) => HKT<F, Either<L, A>> {
-  return ma => F.map(ma, a => eitherRight(a))
+  return <L, A>(fa: HKT<F, A>) => F.map<A, Either<L, A>>(fa, eitherRight)
 }
 
 /**
@@ -147,7 +147,7 @@ export function left<F extends URIS>(F: Functor1<F>): <L, A>(fl: Type<F, L>) =>
 export function left<F>(F: Functor<F>): <L, A>(fl: HKT<F, L>) => HKT<F, Either<L, A>>
 /** @deprecated */
 export function left<F>(F: Functor<F>): <L, A>(fl: HKT<F, L>) => HKT<F, Either<L, A>> {
-  return ml => F.map(ml, l => eitherLeft(l))
+  return <L, A>(fl: HKT<F, L>) => F.map<L, Either<L, A>>(fl, eitherLeft)
 }
 
 /**
diff --git a/src/Exception.ts b/src/Exception.ts
index 00784f71..f412a948 100644
--- a/src/Exception.ts
+++ b/src/Exception.ts
@@ -69,5 +69,5 @@ export const catchError = <A>(ma: IO<A>, handler: (e: Error) => IO<A>): IO<A> =>
  * @since 1.0.0
  */
 export const tryCatch = <A>(ma: IO<A>): IO<Either<Error, A>> => {
-  return catchError(ma.map(a => right(a)), e => io.of(left<Error, A>(e)))
+  return catchError(ma.map<Either<Error, A>>(right), e => io.of(left<Error, A>(e)))
 }
diff --git a/src/IOEither.ts b/src/IOEither.ts
index 2b053da2..4c948404 100644
--- a/src/IOEither.ts
+++ b/src/IOEither.ts
@@ -109,14 +109,14 @@ const bimap = <L, V, A, B>(fa: IOEither<L, A>, f: (l: L) => V, g: (a: A) => B):
  * @since 1.6.0
  */
 export const right = <L, A>(fa: IO<A>): IOEither<L, A> => {
-  return new IOEither(fa.map(a => eitherRight(a)))
+  return new IOEither(fa.map<Either<L, A>>(eitherRight))
 }
 
 /**
  * @since 1.6.0
  */
 export const left = <L, A>(fa: IO<L>): IOEither<L, A> => {
-  return new IOEither(fa.map(a => eitherLeft(a)))
+  return new IOEither(fa.map<Either<L, A>>(eitherLeft))
 }
 
 /**
diff --git a/src/Map.ts b/src/Map.ts
index 781dc317..c4ed32b6 100644
--- a/src/Map.ts
+++ b/src/Map.ts
@@ -4,7 +4,7 @@ import { Either } from './Either'
 import { FilterableWithIndex2C } from './FilterableWithIndex'
 import { Foldable2v2C, Foldable2v3, Foldable2v2, Foldable2v1, Foldable2v } from './Foldable2v'
 import { FoldableWithIndex2C } from './FoldableWithIndex'
-import { Predicate, phantom, tuple } from './function'
+import { Predicate, phantom } from './function'
 import { HKT, Type, Type2, Type3, URIS, URIS2, URIS3 } from './HKT'
 import { Monoid } from './Monoid'
 import { Option, none, some } from './Option'
@@ -106,7 +106,7 @@ export const collect = <K>(O: Ord<K>): (<A, B>(m: Map<K, A>, f: (k: K, a: A) =>
  */
 export const toArray = <K>(O: Ord<K>): (<A>(m: Map<K, A>) => Array<[K, A]>) => {
   const collectO = collect(O)
-  return <A>(m: Map<K, A>): Array<[K, A]> => collectO(m, (k, a: A) => tuple(k, a))
+  return m => collectO(m, (k, a) => [k, a])
 }
 
 /**
@@ -124,7 +124,7 @@ export function toUnfoldable<K, F>(O: Ord<K>, unfoldable: Unfoldable<F>): <A>(d:
   return d => {
     const arr = toArrayO(d)
     const len = arr.length
-    return unfoldable.unfoldr(0, b => (b < len ? some(tuple(arr[b], b + 1)) : none))
+    return unfoldable.unfoldr(0, b => (b < len ? some([arr[b], b + 1]) : none))
   }
 }
 
@@ -176,7 +176,7 @@ export const remove = <K>(S: Setoid<K>): (<A>(k: K, m: Map<K, A>) => Map<K, A>)
 export const pop = <K>(S: Setoid<K>): (<A>(k: K, m: Map<K, A>) => Option<[A, Map<K, A>]>) => {
   const lookupS = lookup(S)
   const removeS = remove(S)
-  return (k, m) => lookupS(k, m).map(a => tuple(a, removeS(k, m)))
+  return (k, m) => lookupS(k, m).map(a => [a, removeS(k, m)])
 }
 
 /**
@@ -191,7 +191,7 @@ export const lookupWithKey = <K>(S: Setoid<K>) => <A>(k: K, m: Map<K, A>): Optio
   while (!(e = entries.next()).done) {
     const [ka, a] = e.value
     if (S.equals(ka, k)) {
-      return some(tuple(ka, a))
+      return some([ka, a])
     }
   }
   return none
diff --git a/src/NonEmptyArray2v.ts b/src/NonEmptyArray2v.ts
index 5f2190a9..763d30b2 100644
--- a/src/NonEmptyArray2v.ts
+++ b/src/NonEmptyArray2v.ts
@@ -281,6 +281,30 @@ const mapWithIndex = <A, B>(fa: NonEmptyArray<A>, f: (i: number, a: A) => B): No
   return fa.map((a, i) => f(i, a))
 }
 
+/**
+ * Append an element to the end of an array, creating a new non empty array
+ *
+ * @example
+ * import { snoc } from 'fp-ts/lib/NonEmptyArray2v'
+ *
+ * assert.deepStrictEqual(snoc([1, 2, 3], 4), [1, 2, 3, 4])
+ *
+ * @since 1.16.0
+ */
+export const snoc: <A>(as: Array<A>, a: A) => NonEmptyArray<A> = A.snoc
+
+/**
+ * Append an element to the front of an array, creating a new non empty array
+ *
+ * @example
+ * import { cons } from 'fp-ts/lib/NonEmptyArray2v'
+ *
+ * assert.deepStrictEqual(cons(1, [2, 3, 4]), [1, 2, 3, 4])
+ *
+ * @since 1.16.0
+ */
+export const cons: <A>(a: A, as: Array<A>) => NonEmptyArray<A> = A.cons
+
 /**
  * @since 1.15.0
  */
diff --git a/src/Reader.ts b/src/Reader.ts
index daaec734..8f4af7b2 100644
--- a/src/Reader.ts
+++ b/src/Reader.ts
@@ -1,5 +1,5 @@
 import { Category2 } from './Category'
-import { identity, tuple } from './function'
+import { identity } from './function'
 import { Monad2 } from './Monad'
 import { Profunctor2 } from './Profunctor'
 import { Strong2 } from './Strong'
@@ -105,11 +105,11 @@ const id = <A>(): Reader<A, A> => {
 }
 
 const first = <A, B, C>(pab: Reader<A, B>): Reader<[A, C], [B, C]> => {
-  return new Reader(([a, c]) => tuple(pab.run(a), c))
+  return new Reader(([a, c]) => [pab.run(a), c])
 }
 
 const second = <A, B, C>(pbc: Reader<B, C>): Reader<[A, B], [A, C]> => {
-  return new Reader(([a, b]) => tuple(a, pbc.run(b)))
+  return new Reader(([a, b]) => [a, pbc.run(b)])
 }
 
 const left = <A, B, C>(pab: Reader<A, B>): Reader<Either<A, C>, Either<B, C>> => {
diff --git a/src/Record.ts b/src/Record.ts
index a59bd512..94c05d0b 100644
--- a/src/Record.ts
+++ b/src/Record.ts
@@ -2,7 +2,7 @@ import { Applicative, Applicative1, Applicative2, Applicative2C, Applicative3, A
 import { Separated } from './Compactable'
 import { Either } from './Either'
 import { Foldable, Foldable1, Foldable2, Foldable3 } from './Foldable'
-import { Predicate, tuple, Refinement } from './function'
+import { Predicate, Refinement } from './function'
 import { HKT, Type, Type2, Type3, URIS, URIS2, URIS3 } from './HKT'
 import { getDictionaryMonoid, Monoid } from './Monoid'
 import { none, Option, some as optionSome } from './Option'
@@ -48,7 +48,7 @@ export function collect<A, B>(d: Record<string, A>, f: (k: string, a: A) => B):
 export function toArray<K extends string, A>(d: Record<K, A>): Array<[K, A]>
 export function toArray<A>(d: Record<string, A>): Array<[string, A]>
 export function toArray<A>(d: Record<string, A>): Array<[string, A]> {
-  return collect(d, (k, a: A) => tuple(k, a))
+  return collect(d, (k, a: A) => [k, a])
 }
 
 /**
@@ -64,7 +64,7 @@ export function toUnfoldable<F>(unfoldable: Unfoldable<F>): <A>(d: Record<string
   return d => {
     const arr = toArray(d)
     const len = arr.length
-    return unfoldable.unfoldr(0, b => (b < len ? optionSome(tuple(arr[b], b + 1)) : none))
+    return unfoldable.unfoldr(0, b => (b < len ? optionSome([arr[b], b + 1]) : none))
   }
 }
 
@@ -110,7 +110,7 @@ export function remove<A>(k: string, d: Record<string, A>): Record<string, A> {
  */
 export const pop = <A>(k: string, d: Record<string, A>): Option<[A, Record<string, A>]> => {
   const a = lookup(k, d)
-  return a.isNone() ? none : optionSome(tuple(a.value, remove(k, d)))
+  return a.isNone() ? none : optionSome([a.value, remove(k, d)])
 }
 
 /**
diff --git a/src/StateT.ts b/src/StateT.ts
index 12b25d33..008fac72 100644
--- a/src/StateT.ts
+++ b/src/StateT.ts
@@ -4,7 +4,7 @@ import { Functor, Functor1, Functor2, Functor3 } from './Functor'
 import { HKT, Type, Type2, Type3, URIS, URIS2, URIS3 } from './HKT'
 import { Monad, Monad1, Monad2, Monad3 } from './Monad'
 import { State } from './State'
-import { Endomorphism, tuple } from './function'
+import { Endomorphism } from './function'
 
 export interface StateT2v<M> {
   readonly map: <S, A, B>(fa: (s: S) => HKT<M, [A, S]>, f: (a: A) => B) => (s: S) => HKT<M, [B, S]>
@@ -69,7 +69,7 @@ export function get2v<F extends URIS2>(F: Applicative2<F>): <S, L>(s: S) => Type
 export function get2v<F extends URIS>(F: Applicative1<F>): <S>(s: S) => Type<F, [S, S]>
 export function get2v<F>(F: Applicative<F>): <S>(s: S) => HKT<F, [S, S]>
 export function get2v<F>(F: Applicative<F>): <S>(s: S) => HKT<F, [S, S]> {
-  return s => F.of(tuple(s, s))
+  return s => F.of([s, s])
 }
 
 /**
@@ -80,7 +80,7 @@ export function put<F extends URIS2>(F: Applicative2<F>): <S>(s: S) => <L>() =>
 export function put<F extends URIS>(F: Applicative1<F>): <S>(s: S) => () => Type<F, [void, S]>
 export function put<F>(F: Applicative<F>): <S>(s: S) => () => HKT<F, [void, S]>
 export function put<F>(F: Applicative<F>): <S>(s: S) => () => HKT<F, [void, S]> {
-  return s => () => F.of(tuple(undefined, s))
+  return s => () => F.of([undefined, s])
 }
 
 /**
@@ -95,7 +95,7 @@ export function modify<F extends URIS2>(
 export function modify<F extends URIS>(F: Applicative1<F>): <S>(f: Endomorphism<S>) => (s: S) => Type<F, [void, S]>
 export function modify<F>(F: Applicative<F>): <S>(f: Endomorphism<S>) => (s: S) => HKT<F, [void, S]>
 export function modify<F>(F: Applicative<F>): <S>(f: Endomorphism<S>) => (s: S) => HKT<F, [void, S]> {
-  return f => s => F.of(tuple(undefined, f(s)))
+  return f => s => F.of([undefined, f(s)])
 }
 
 /**
@@ -108,7 +108,7 @@ export function gets<F extends URIS2>(F: Applicative2<F>): <S, A>(f: (s: S) => A
 export function gets<F extends URIS>(F: Applicative1<F>): <S, A>(f: (s: S) => A) => (s: S) => Type<F, [A, S]>
 export function gets<F>(F: Applicative<F>): <S, A>(f: (s: S) => A) => (s: S) => HKT<F, [A, S]>
 export function gets<F>(F: Applicative<F>): <S, A>(f: (s: S) => A) => (s: S) => HKT<F, [A, S]> {
-  return f => s => F.of(tuple(f(s), s))
+  return f => s => F.of([f(s), s])
 }
 
 /**
@@ -136,7 +136,7 @@ export function liftF<F extends URIS2>(F: Functor2<F>): <L, S, A>(fa: Type2<F, L
 export function liftF<F extends URIS>(F: Functor1<F>): <S, A>(fa: Type<F, A>) => (s: S) => Type<F, [A, S]>
 export function liftF<F>(F: Functor<F>): <S, A>(fa: HKT<F, A>) => (s: S) => HKT<F, [A, S]>
 export function liftF<F>(F: Functor<F>): <S, A>(fa: HKT<F, A>) => (s: S) => HKT<F, [A, S]> {
-  return fa => s => F.map(fa, a => tuple(a, s))
+  return fa => s => F.map(fa, a => [a, s])
 }
 
 /**
@@ -148,9 +148,9 @@ export function getStateT2v<M extends URIS>(M: Monad1<M>): StateT2v1<M>
 export function getStateT2v<M>(M: Monad<M>): StateT2v<M>
 export function getStateT2v<M>(M: Monad<M>): StateT2v<M> {
   return {
-    map: (fa, f) => s => M.map(fa(s), ([a, s1]) => tuple(f(a), s1)),
-    of: a => s => M.of(tuple(a, s)),
-    ap: (fab, fa) => s => M.chain(fab(s), ([f, s]) => M.map(fa(s), ([a, s]) => tuple(f(a), s))),
+    map: (fa, f) => s => M.map(fa(s), ([a, s1]) => [f(a), s1]),
+    of: a => s => M.of([a, s]),
+    ap: (fab, fa) => s => M.chain(fab(s), ([f, s]) => M.map(fa(s), ([a, s]) => [f(a), s])),
     chain: (fa, f) => s => M.chain(fa(s), ([a, s1]) => f(a)(s1))
   }
 }
@@ -237,7 +237,7 @@ export function map<F>(
 export function map<F>(
   F: Functor<F>
 ): <S, A, B>(f: (a: A) => B, fa: (s: S) => HKT<F, [A, S]>) => (s: S) => HKT<F, [B, S]> {
-  return (f, fa) => s => F.map(fa(s), ([a, s1]) => tuple(f(a), s1))
+  return (f, fa) => s => F.map(fa(s), ([a, s1]) => [f(a), s1])
 }
 
 /**
@@ -269,7 +269,7 @@ export function ap<F>(
 export function ap<F>(
   F: Chain<F>
 ): <S, A, B>(fab: (s: S) => HKT<F, [(a: A) => B, S]>, fa: (s: S) => HKT<F, [A, S]>) => (s: S) => HKT<F, [B, S]> {
-  return (fab, fa) => s => F.chain(fab(s), ([f, s]) => F.map(fa(s), ([a, s]) => tuple(f(a), s)))
+  return (fab, fa) => s => F.chain(fab(s), ([f, s]) => F.map(fa(s), ([a, s]) => [f(a), s]))
 }
 
 /**
@@ -348,7 +348,7 @@ export function of<F extends URIS>(F: Applicative1<F>): <S, A>(a: A) => (s: S) =
 export function of<F>(F: Applicative<F>): <S, A>(a: A) => (s: S) => HKT<F, [A, S]>
 /** @deprecated */
 export function of<F>(F: Applicative<F>): <S, A>(a: A) => (s: S) => HKT<F, [A, S]> {
-  return a => s => F.of(tuple(a, s))
+  return a => s => F.of([a, s])
 }
 
 /**
@@ -364,5 +364,5 @@ export function get<F extends URIS>(F: Applicative1<F>): <S>() => (s: S) => Type
 export function get<F>(F: Applicative<F>): <S>() => (s: S) => HKT<F, [S, S]>
 /** @deprecated */
 export function get<F>(F: Applicative<F>): <S>() => (s: S) => HKT<F, [S, S]> {
-  return () => s => F.of(tuple(s, s))
+  return () => s => F.of([s, s])
 }
diff --git a/src/StrMap.ts b/src/StrMap.ts
index aa2b40ae..ea10b260 100644
--- a/src/StrMap.ts
+++ b/src/StrMap.ts
@@ -8,7 +8,7 @@ import { FilterableWithIndex1 } from './FilterableWithIndex'
 import { Foldable, Foldable1, Foldable2, Foldable3 } from './Foldable'
 import { Foldable2v1 } from './Foldable2v'
 import { FoldableWithIndex1 } from './FoldableWithIndex'
-import { Predicate, Refinement, tuple } from './function'
+import { Predicate, Refinement } from './function'
 import { FunctorWithIndex1 } from './FunctorWithIndex'
 import { HKT, Type, Type2, Type3, URIS, URIS2, URIS3 } from './HKT'
 import { Monoid } from './Monoid'
@@ -398,7 +398,7 @@ export const remove = <A>(k: string, d: StrMap<A>): StrMap<A> => {
  * @since 1.0.0
  */
 export const pop = <A>(k: string, d: StrMap<A>): Option<[A, StrMap<A>]> => {
-  return R.pop(k, d.value).map(([a, d]) => tuple(a, new StrMap(d)))
+  return R.pop(k, d.value).map(([a, d]) => [a, new StrMap(d)])
 }
 
 /**
diff --git a/src/Strong.ts b/src/Strong.ts
index 48e68c3a..d858a340 100644
--- a/src/Strong.ts
+++ b/src/Strong.ts
@@ -26,7 +26,7 @@
  * Adapted from https://github.com/purescript/purescript-profunctor/blob/master/src/Data/Profunctor/Strong.purs
  */
 import { Category, Category2, Category3, Category4 } from './Category'
-import { identity, tuple } from './function'
+import { identity } from './function'
 import { HKT2, Type2, Type3, URIS2, URIS3, URIS4, Type4 } from './HKT'
 import { Profunctor, Profunctor2, Profunctor3, Profunctor4 } from './Profunctor'
 
@@ -124,7 +124,7 @@ export function fanout<F>(
 ): <A, B, C>(pab: HKT2<F, A, B>, pac: HKT2<F, A, C>) => HKT2<F, A, [B, C]> {
   const splitStrongF = splitStrong(F)
   return <A, B, C>(pab: HKT2<F, A, B>, pac: HKT2<F, A, C>): HKT2<F, A, [B, C]> => {
-    const split: HKT2<F, A, [A, A]> = F.promap(F.id<A>(), identity, a => tuple(a, a))
+    const split: HKT2<F, A, [A, A]> = F.promap(F.id<A>(), identity, a => [a, a])
     return F.compose(
       splitStrongF(pab, pac),
       split
diff --git a/src/Task.ts b/src/Task.ts
index f582d696..fb9083b6 100644
--- a/src/Task.ts
+++ b/src/Task.ts
@@ -136,7 +136,7 @@ export const getMonoid = <A>(M: Monoid<A>): Monoid<Task<A>> => {
  * @since 1.0.0
  */
 export const tryCatch = <L, A>(f: Lazy<Promise<A>>, onrejected: (reason: unknown) => L): Task<Either<L, A>> => {
-  return new Task(() => f().then(a => right<L, A>(a), reason => left<L, A>(onrejected(reason))))
+  return new Task(() => f().then<Either<L, A>, Either<L, A>>(right, reason => left(onrejected(reason))))
 }
 
 /**
diff --git a/src/TaskEither.ts b/src/TaskEither.ts
index bbbad21c..797632ee 100644
--- a/src/TaskEither.ts
+++ b/src/TaskEither.ts
@@ -182,14 +182,14 @@ const bimap = <L, V, A, B>(fa: TaskEither<L, A>, f: (l: L) => V, g: (a: A) => B)
  * @since 1.0.0
  */
 export const right = <L, A>(fa: Task<A>): TaskEither<L, A> => {
-  return new TaskEither(fa.map(a => eitherRight(a)))
+  return new TaskEither(fa.map<Either<L, A>>(eitherRight))
 }
 
 /**
  * @since 1.0.0
  */
 export const left = <L, A>(fl: Task<L>): TaskEither<L, A> => {
-  return new TaskEither(fl.map(l => eitherLeft(l)))
+  return new TaskEither(fl.map<Either<L, A>>(eitherLeft))
 }
 
 /**
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/Traced.ts b/src/Traced.ts
new file mode 100644
index 00000000..5e16d644
--- /dev/null
+++ b/src/Traced.ts
@@ -0,0 +1,92 @@
+import { Comonad2C } from './Comonad'
+import { Monoid } from './Monoid'
+import { Functor2 } from './Functor'
+import { phantom, tuple } from './function'
+
+declare module './HKT' {
+  interface URI2HKT2<L, A> {
+    Traced: Traced<L, A>
+  }
+}
+
+export const URI = 'Traced'
+
+export type URI = typeof URI
+
+/**
+ * @since 1.16.0
+ */
+export class Traced<P, A> {
+  readonly _A!: A
+  readonly _L!: P
+  readonly _URI!: URI
+  constructor(readonly run: (p: P) => A) {}
+  map<B>(f: (a: A) => B): Traced<P, B> {
+    return new Traced((p: P) => f(this.run(p)))
+  }
+}
+
+/**
+ * Extracts a value at a relative position which depends on the current value.
+ * @since 1.16.0
+ */
+export const tracks = <P, A>(M: Monoid<P>, f: (a: A) => P) => (wa: Traced<P, A>): A => {
+  return wa.run(f(wa.run(M.empty)))
+}
+
+/**
+ * Get the current position
+ * @since 1.16.0
+ */
+export const listen = <P, A>(wa: Traced<P, A>): Traced<P, [A, P]> => {
+  return new Traced(e => tuple(wa.run(e), e))
+}
+
+/**
+ * Get a value which depends on the current position
+ * @since 1.16.0
+ */
+export const listens = <P, A, B>(wa: Traced<P, A>, f: (p: P) => B): Traced<P, [A, B]> => {
+  return new Traced(e => tuple(wa.run(e), f(e)))
+}
+
+/**
+ * Apply a function to the current position
+ * @since 1.16.0
+ */
+export const censor = <P, A>(wa: Traced<P, A>, f: (p: P) => P): Traced<P, A> => {
+  return new Traced(e => wa.run(f(e)))
+}
+
+/**
+ * @since 1.16.0
+ */
+export function getComonad<P>(monoid: Monoid<P>): Comonad2C<URI, P> {
+  function extend<A, B>(wa: Traced<P, A>, f: (wa: Traced<P, A>) => B): Traced<P, B> {
+    return new Traced((p1: P) => f(new Traced((p2: P) => wa.run(monoid.concat(p1, p2)))))
+  }
+
+  function extract<A>(wa: Traced<P, A>): A {
+    return wa.run(monoid.empty)
+  }
+
+  return {
+    URI,
+    _L: phantom,
+    map,
+    extend,
+    extract
+  }
+}
+
+function map<P, A, B>(wa: Traced<P, A>, f: (a: A) => B): Traced<P, B> {
+  return wa.map(f)
+}
+
+/**
+ * @since 1.16.0
+ */
+export const traced: Functor2<URI> = {
+  URI,
+  map
+}
diff --git a/src/Unfoldable.ts b/src/Unfoldable.ts
index 34d6c4f4..88aca38a 100644
--- a/src/Unfoldable.ts
+++ b/src/Unfoldable.ts
@@ -5,7 +5,6 @@ import { Applicative, Applicative1, Applicative2, Applicative2C, Applicative3, A
 import { HKT, Type, Type2, Type3, URIS, URIS2, URIS3 } from './HKT'
 import { Option, none, option } from './Option'
 import { Traversable, Traversable1, sequence } from './Traversable'
-import { constant, tuple } from './function'
 
 /**
  * @since 1.0.0
@@ -61,9 +60,9 @@ export function replicate<F extends URIS2, L>(U: Unfoldable2C<F, L>): <A>(a: A,
 export function replicate<F extends URIS>(U: Unfoldable1<F>): <A>(a: A, n: number) => Type<F, A>
 export function replicate<F>(U: Unfoldable<F>): <A>(a: A, n: number) => HKT<F, A>
 export function replicate<F>(U: Unfoldable<F>): <A>(a: A, n: number) => HKT<F, A> {
-  return (a, n) => {
-    function step(n: number) {
-      return n <= 0 ? none : option.of(tuple(a, n - 1))
+  return <A>(a: A, n: number) => {
+    function step(n: number): Option<[A, number]> {
+      return n <= 0 ? none : option.of([a, n - 1])
     }
     return U.unfoldr(n, step)
   }
@@ -85,7 +84,7 @@ export function empty<F extends URIS2, L, A>(U: Unfoldable2<F> | Unfoldable2C<F,
 export function empty<F extends URIS, A>(U: Unfoldable1<F>): Type<F, A>
 export function empty<F, A>(U: Unfoldable<F>): HKT<F, A>
 export function empty<F, A>(U: Unfoldable<F>): HKT<F, A> {
-  return U.unfoldr(undefined, constant(none))
+  return U.unfoldr(undefined, () => none)
 }
 
 /**
diff --git a/src/Validation.ts b/src/Validation.ts
index d4212cf9..aef961e7 100644
--- a/src/Validation.ts
+++ b/src/Validation.ts
@@ -12,7 +12,7 @@ import { Compactable2C, Separated } from './Compactable'
 import { Either } from './Either'
 import { Filterable2C } from './Filterable'
 import { Foldable2v2 } from './Foldable2v'
-import { phantom, Predicate, toString, Refinement } from './function'
+import { phantom, Predicate, toString, Refinement, Lazy } from './function'
 import { Functor2 } from './Functor'
 import { HKT } from './HKT'
 import { Monad2C } from './Monad'
@@ -306,6 +306,37 @@ export const fromEither = <L, A>(e: Either<L, A>): Validation<L, A> => {
   return e.isLeft() ? failure(e.value) : success(e.value)
 }
 
+/**
+ * Constructs a new `Validation` from a function that might throw
+ *
+ * @example
+ * import { Validation, failure, success, tryCatch } from 'fp-ts/lib/Validation'
+ *
+ * const unsafeHead = <A>(as: Array<A>): A => {
+ *   if (as.length > 0) {
+ *     return as[0]
+ *   } else {
+ *     throw new Error('empty array')
+ *   }
+ * }
+ *
+ * const head = <A>(as: Array<A>): Validation<Error, A> => {
+ *   return tryCatch(() => unsafeHead(as), e => (e instanceof Error ? e : new Error('unknown error')))
+ * }
+ *
+ * assert.deepStrictEqual(head([]), failure(new Error('empty array')))
+ * assert.deepStrictEqual(head([1, 2, 3]), success(1))
+ *
+ * @since 1.16.0
+ */
+export const tryCatch = <L, A>(f: Lazy<A>, onError: (e: unknown) => L): Validation<L, A> => {
+  try {
+    return success(f())
+  } catch (e) {
+    return failure(onError(e))
+  }
+}
+
 /**
  * @since 1.0.0
  */
diff --git a/src/Writer.ts b/src/Writer.ts
index 34386814..7b678080 100644
--- a/src/Writer.ts
+++ b/src/Writer.ts
@@ -2,7 +2,7 @@ import { Functor2 } from './Functor'
 import { Monad2C } from './Monad'
 import { Monoid } from './Monoid'
 import { Semigroup } from './Semigroup'
-import { phantom, tuple } from './function'
+import { phantom } from './function'
 
 declare module './HKT' {
   interface URI2HKT2<L, A> {
@@ -77,7 +77,7 @@ export const tell = <W>(w: W): Writer<W, void> => {
 export const listen = <W, A>(fa: Writer<W, A>): Writer<W, [A, W]> => {
   return new Writer(() => {
     const [a, w] = fa.run()
-    return [tuple(a, w), w]
+    return [[a, w], w]
   })
 }
 
@@ -101,7 +101,7 @@ export const pass = <W, A>(fa: Writer<W, [A, (w: W) => W]>): Writer<W, A> => {
 export const listens = <W, A, B>(fa: Writer<W, A>, f: (w: W) => B): Writer<W, [A, B]> => {
   return new Writer(() => {
     const [a, w] = fa.run()
-    return [tuple(a, f(w)), w]
+    return [[a, f(w)], w]
   })
 }
 
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 e148ab73..5345b4e4 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -168,6 +168,8 @@ import * as these from './These'
 export { these }
 import * as trace from './Trace'
 export { trace }
+import * as traced from './Traced'
+export { traced }
 import * as traversable from './Traversable'
 export { traversable }
 import * as traversable2v from './Traversable2v'
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/Array.ts b/test/Array.ts
index 144f67ff..11a271bc 100644
--- a/test/Array.ts
+++ b/test/Array.ts
@@ -1,4 +1,5 @@
 import * as assert from 'assert'
+import * as fc from 'fast-check'
 import {
   array,
   catOptions,
@@ -62,14 +63,16 @@ import {
   union,
   intersection,
   difference,
-  unsafeUpdateAt
+  unsafeUpdateAt,
+  findFirstMap,
+  findLastMap
 } from '../src/Array'
 import { left, right } from '../src/Either'
 import { fold as foldMonoid, monoidSum, monoidString } from '../src/Monoid'
-import { option, Option, none, some, isSome } from '../src/Option'
+import { option, Option, none, some, isSome, getSetoid, fromPredicate } from '../src/Option'
 import { contramap as contramapOrd, ordNumber, ordString } from '../src/Ord'
 import { contramap, getArraySetoid, setoidBoolean, setoidNumber, setoidString, Setoid } from '../src/Setoid'
-import { identity, tuple, constTrue } from '../src/function'
+import { identity, tuple, constTrue, Predicate } from '../src/function'
 import * as I from '../src/Identity'
 import * as F from '../src/Foldable'
 import * as C from '../src/Const'
@@ -143,7 +146,7 @@ describe('Array', () => {
   })
 
   it('unfoldr', () => {
-    const as = array.unfoldr(5, n => (n > 0 ? some(tuple(n, n - 1)) : none))
+    const as = array.unfoldr(5, n => (n > 0 ? some([n, n - 1]) : none))
     assert.deepStrictEqual(as, [5, 4, 3, 2, 1])
   })
 
@@ -271,6 +274,18 @@ describe('Array', () => {
     assert.deepStrictEqual(findFirst([null, 'a'], x => x === null), some(null))
   })
 
+  const optionStringSetoid = getSetoid(setoidString)
+  const multipleOf3: Predicate<number> = (x: number) => x % 3 === 0
+  const multipleOf3AsString = (x: number) => fromPredicate(multipleOf3)(x).map(x => `${x}`)
+
+  it('`findFirstMap(arr, fun)` is equivalent to map and `head(mapOption(arr, fun)`', () => {
+    fc.assert(
+      fc.property(fc.array(fc.integer()), arr =>
+        optionStringSetoid.equals(findFirstMap(arr, multipleOf3AsString), head(mapOption(arr, multipleOf3AsString)))
+      )
+    )
+  })
+
   it('findLast', () => {
     assert.deepStrictEqual(findLast([], x => x === 2), none)
     assert.deepStrictEqual(findLast([{ a: 1, b: 1 }, { a: 1, b: 2 }], x => x.a === 1), some({ a: 1, b: 2 }))
@@ -278,6 +293,14 @@ describe('Array', () => {
     assert.deepStrictEqual(findLast(['a', null], x => x === null), some(null))
   })
 
+  it('`findLastMap(arr, fun)` is equivalent to `last(mapOption(arr, fun))`', () => {
+    fc.assert(
+      fc.property(fc.array(fc.integer()), arr =>
+        optionStringSetoid.equals(findLastMap(arr, multipleOf3AsString), last(mapOption(arr, multipleOf3AsString)))
+      )
+    )
+  })
+
   it('findLastIndex', () => {
     interface X {
       a: number
diff --git a/test/NonEmptyArray2v.ts b/test/NonEmptyArray2v.ts
index dc47c52a..1eb1bbbe 100644
--- a/test/NonEmptyArray2v.ts
+++ b/test/NonEmptyArray2v.ts
@@ -23,7 +23,9 @@ import {
   updateAt,
   filter,
   filterWithIndex,
-  fromNonEmptyArray
+  fromNonEmptyArray,
+  cons,
+  snoc
 } from '../src/NonEmptyArray2v'
 import { none, option, some, isSome } from '../src/Option'
 import { ordNumber } from '../src/Ord'
@@ -379,4 +381,12 @@ describe.only('NonEmptyArray2v', () => {
       nonEmptyArray.traverseWithIndex(I.identity)(ta, (i, a) => new I.Identity(f(i, a))).value
     )
   })
+
+  it('cons', () => {
+    assert.deepStrictEqual(cons(1, [2, 3, 4]), [1, 2, 3, 4])
+  })
+
+  it('snoc', () => {
+    assert.deepStrictEqual(snoc([1, 2, 3], 4), [1, 2, 3, 4])
+  })
 })
diff --git a/test/Traced.ts b/test/Traced.ts
new file mode 100644
index 00000000..6bca54fe
--- /dev/null
+++ b/test/Traced.ts
@@ -0,0 +1,132 @@
+import * as assert from 'assert'
+import { getStructMonoid, monoidAny, Monoid } from '../src/Monoid'
+import { Traced, traced, getComonad, tracks, listen, listens, censor } from '../src/Traced'
+
+// Adapted from https://chshersh.github.io/posts/2019-03-25-comonadic-builders
+
+interface Settings {
+  settingsHasLibrary: boolean
+  settingsGitHub: boolean
+  settingsTravis: boolean
+}
+
+const M: Monoid<Settings> = getStructMonoid({
+  settingsHasLibrary: monoidAny,
+  settingsGitHub: monoidAny,
+  settingsTravis: monoidAny
+})
+
+const C = getComonad(M)
+
+interface Project {
+  projectName: string
+  projectHasLibrary: boolean
+  projectGitHub: boolean
+  projectTravis: boolean
+}
+
+interface ProjectBuilder extends Traced<Settings, Project> {}
+
+const buildProject = (projectName: string): ProjectBuilder =>
+  new Traced(settings => ({
+    projectName,
+    projectHasLibrary: settings.settingsHasLibrary,
+    projectGitHub: settings.settingsGitHub,
+    projectTravis: settings.settingsTravis
+  }))
+
+const hasLibraryB = (wa: ProjectBuilder): Project => {
+  const p = { ...M.empty, settingsHasLibrary: true }
+  return wa.run(p)
+}
+
+const gitHubB = (wa: ProjectBuilder): Project => {
+  const p = { ...M.empty, settingsGitHub: true }
+  return wa.run(p)
+}
+
+const getProjectName = (project: Project): string => project.projectName
+
+describe('Traced', () => {
+  it('map', () => {
+    const wa = buildProject('myproject')
+    assert.deepStrictEqual(traced.map(wa, getProjectName).run(M.empty), 'myproject')
+  })
+
+  it('getComonad', () => {
+    const wa = buildProject('myproject')
+    assert.deepStrictEqual(C.extract(wa), {
+      projectName: 'myproject',
+      projectHasLibrary: false,
+      projectGitHub: false,
+      projectTravis: false
+    })
+    assert.deepStrictEqual(C.extract(C.extend(wa, hasLibraryB)), {
+      projectName: 'myproject',
+      projectHasLibrary: true,
+      projectGitHub: false,
+      projectTravis: false
+    })
+  })
+
+  it('tracks', () => {
+    const travisB = tracks(M, (project: Project): Settings => ({ ...M.empty, settingsTravis: project.projectGitHub }))
+    assert.deepStrictEqual(C.extract(C.extend(buildProject('travis'), travisB)), {
+      projectName: 'travis',
+      projectHasLibrary: false,
+      projectGitHub: false,
+      projectTravis: false
+    })
+    assert.deepStrictEqual(C.extract(C.extend(C.extend(buildProject('github-travis'), gitHubB), travisB)), {
+      projectName: 'github-travis',
+      projectHasLibrary: false,
+      projectGitHub: true,
+      projectTravis: true
+    })
+  })
+
+  it('listen', () => {
+    assert.deepStrictEqual(C.extract(listen(buildProject('myproject'))), [
+      {
+        projectName: 'myproject',
+        projectHasLibrary: false,
+        projectGitHub: false,
+        projectTravis: false
+      },
+      {
+        settingsHasLibrary: false,
+        settingsGitHub: false,
+        settingsTravis: false
+      }
+    ])
+  })
+
+  it('listens', () => {
+    assert.deepStrictEqual(C.extract(listens(buildProject('myproject'), settings => settings.settingsTravis)), [
+      {
+        projectName: 'myproject',
+        projectHasLibrary: false,
+        projectGitHub: false,
+        projectTravis: false
+      },
+      false
+    ])
+  })
+
+  it('censor', () => {
+    assert.deepStrictEqual(
+      C.extract(
+        censor(buildProject('myproject'), settings => ({
+          ...settings,
+          settingsHasLibrary: !settings.settingsHasLibrary
+        }))
+      ),
+      {
+        projectName: 'myproject',
+        projectHasLibrary: true,
+        projectGitHub: false,
+        projectTravis: false
+      }
+    )
+  })
+})
diff --git a/test/Tree.ts b/test/Tree.ts
index 48f289bf..9657d687 100644
--- a/test/Tree.ts
+++ b/test/Tree.ts
@@ -1,6 +1,6 @@
 import * as assert from 'assert'
 import * as F from '../src/Foldable'
-import { identity, tuple } from '../src/function'
+import { identity } from '../src/function'
 import * as I from '../src/Identity'
 import { monoidString } from '../src/Monoid'
 import { setoidNumber, Setoid, contramap } from '../src/Setoid'
@@ -158,13 +158,13 @@ describe('Tree', () => {
   })
 
   it('unfoldTree', () => {
-    const fa = unfoldTree(1, b => tuple(b, b < 3 ? [b + 1, b + 2] : []))
+    const fa = unfoldTree(1, b => [b, b < 3 ? [b + 1, b + 2] : []])
     const expected = new Tree(1, [new Tree(2, [new Tree(3, []), new Tree(4, [])]), new Tree(3, [])])
     assert.deepStrictEqual(fa, expected)
   })
 
   it('unfoldTreeM', () => {
-    const fa = unfoldTreeM(I.identity)(1, b => I.identity.of(tuple(b, b < 3 ? [b + 1, b + 2] : [])))
+    const fa = unfoldTreeM(I.identity)(1, b => I.identity.of([b, b < 3 ? [b + 1, b + 2] : []]))
     const expected = I.identity.of(new Tree(1, [new Tree(2, [new Tree(3, []), new Tree(4, [])]), new Tree(3, [])]))
     assert.deepStrictEqual(fa, expected)
   })
diff --git a/test/Validation.ts b/test/Validation.ts
index 1ae19c65..12ac55c6 100644
--- a/test/Validation.ts
+++ b/test/Validation.ts
@@ -23,7 +23,8 @@ import {
   isFailure,
   isSuccess,
   success,
-  validation
+  validation,
+  tryCatch
 } from '../src/Validation'
 
 const p = (n: number): boolean => n > 2
@@ -323,4 +324,21 @@ describe('Validation', () => {
       )
     })
   })
+
+  it('tryCatch', () => {
+    const v1 = tryCatch(
+      () => {
+        throw new Error('foo')
+      },
+      () => 'error'
+    )
+    assert.deepStrictEqual(v1, failure('error'))
+    const v2 = tryCatch(
+      () => {
+        return 1
+      },
+      () => 'error'
+    )
+    assert.deepStrictEqual(v2, success(1))
+  })
 })