Repository: gcanti/fp-ts
Pull request #1204 (2020-05-11 08:30:19)

version 2.6


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 725c58ea..5ccc5447 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -14,6 +14,40 @@
 **Note**: Gaps between patch versions are faulty/broken releases. **Note**: A feature tagged as Experimental is in a
 high state of flux, you're at risk of it changing without notice.
 
+# 2.6.0
+
+- **New Feature**
+  - add W variants, closes #904 (@gcanti)
+  - `Const`
+    - add missing instances, #1201 (@gcanti)
+  - `Date`
+    - add `eqDate`, `eqMonth`, `eqYear` (@gcanti)
+  - `Either`
+    - add `getOrElseW` (@gcanti)
+    - add `chainW` (@gcanti)
+  - `Eq`
+    - add `getMonoid` (@gcanti)
+  - `IOEither`
+    - add `getOrElseW` (@gcanti)
+    - add `chainW` (@gcanti)
+  - `Option`
+    - add `getOrElseW` (@gcanti)
+  - `Reader`
+    - add `chainW` (@gcanti)
+  - `ReaderEither`
+    - add `getOrElseW` (@gcanti)
+    - add `chainW` (@gcanti)
+  - `ReaderTaskEither`
+    - add `getOrElseW` (@gcanti)
+    - add `chainW` (@gcanti)
+  - `StateReaderTaskEither`
+    - add `chainW` (@gcanti)
+  - `TaskEither`
+    - add `getOrElseW` (@gcanti)
+    - add `chainW` (@gcanti)
+  - `Tree`
+    - add `fold` function (@gcanti)
+
 # 2.5.4
 
 - **Polish**
diff --git a/docs/modules/Const.ts.md b/docs/modules/Const.ts.md
index a1fab40b..7b2bc491 100644
--- a/docs/modules/Const.ts.md
+++ b/docs/modules/Const.ts.md
@@ -6,6 +6,12 @@ parent: Modules
 
 # Const overview
 
+The `Const` type constructor, which wraps its first type argument and ignores its second.
+That is, `Const<E, A>` is isomorphic to `E` for any `A`.
+
+`Const` has some useful instances. For example, the `Applicative` instance allows us to collect results using a `Monoid`
+while ignoring return values.
+
 Added in v2.0.0
 
 ---
@@ -19,7 +25,15 @@ Added in v2.0.0
 - [contramap](#contramap)
 - [getApplicative](#getapplicative)
 - [getApply](#getapply)
+- [getBooleanAlgebra](#getbooleanalgebra)
+- [getBounded](#getbounded)
 - [getEq](#geteq)
+- [getHeytingAlgebra](#getheytingalgebra)
+- [getMonoid](#getmonoid)
+- [getOrd](#getord)
+- [getRing](#getring)
+- [getSemigroup](#getsemigroup)
+- [getSemiring](#getsemiring)
 - [getShow](#getshow)
 - [make](#make)
 - [map](#map)
@@ -61,7 +75,7 @@ Added in v2.0.0
 **Signature**
 
 ```ts
-export declare const const_: Functor2<'Const'> & Contravariant2<'Const'>
+export declare const const_: Functor2<'Const'> & Contravariant2<'Const'> & Bifunctor2<'Const'>
 ```
 
 Added in v2.0.0
@@ -96,6 +110,26 @@ export declare function getApply<E>(S: Semigroup<E>): Apply2C<URI, E>
 
 Added in v2.0.0
 
+# getBooleanAlgebra
+
+**Signature**
+
+```ts
+export declare const getBooleanAlgebra: <E, A>(H: BooleanAlgebra<E>) => BooleanAlgebra<Const<E, A>>
+```
+
+Added in v2.6.0
+
+# getBounded
+
+**Signature**
+
+```ts
+export declare const getBounded: <E, A>(B: Bounded<E>) => Bounded<Const<E, A>>
+```
+
+Added in v2.6.0
+
 # getEq
 
 **Signature**
@@ -106,6 +140,66 @@ export declare const getEq: <E, A>(E: Eq<E>) => Eq<Const<E, A>>
 
 Added in v2.0.0
 
+# getHeytingAlgebra
+
+**Signature**
+
+```ts
+export declare const getHeytingAlgebra: <E, A>(H: HeytingAlgebra<E>) => HeytingAlgebra<Const<E, A>>
+```
+
+Added in v2.6.0
+
+# getMonoid
+
+**Signature**
+
+```ts
+export declare const getMonoid: <E, A>(M: Monoid<E>) => Monoid<Const<E, A>>
+```
+
+Added in v2.6.0
+
+# getOrd
+
+**Signature**
+
+```ts
+export declare const getOrd: <E, A>(O: Ord<E>) => Ord<Const<E, A>>
+```
+
+Added in v2.6.0
+
+# getRing
+
+**Signature**
+
+```ts
+export declare const getRing: <E, A>(S: Ring<E>) => Ring<Const<E, A>>
+```
+
+Added in v2.6.0
+
+# getSemigroup
+
+**Signature**
+
+```ts
+export declare const getSemigroup: <E, A>(S: Semigroup<E>) => Semigroup<Const<E, A>>
+```
+
+Added in v2.6.0
+
+# getSemiring
+
+**Signature**
+
+```ts
+export declare const getSemiring: <E, A>(S: Semiring<E>) => Semiring<Const<E, A>>
+```
+
+Added in v2.6.0
+
 # getShow
 
 **Signature**
diff --git a/docs/modules/Date.ts.md b/docs/modules/Date.ts.md
index 244ae59e..2d451bd6 100644
--- a/docs/modules/Date.ts.md
+++ b/docs/modules/Date.ts.md
@@ -13,6 +13,9 @@ Added in v2.0.0
 <h2 class="text-delta">Table of contents</h2>
 
 - [create](#create)
+- [eqDate](#eqdate)
+- [eqMonth](#eqmonth)
+- [eqYear](#eqyear)
 - [now](#now)
 
 ---
@@ -29,6 +32,36 @@ export declare const create: IO<Date>
 
 Added in v2.0.0
 
+# eqDate
+
+**Signature**
+
+```ts
+export declare const eqDate: Eq<Date>
+```
+
+Added in v2.6.0
+
+# eqMonth
+
+**Signature**
+
+```ts
+export declare const eqMonth: Eq<Date>
+```
+
+Added in v2.6.0
+
+# eqYear
+
+**Signature**
+
+```ts
+export declare const eqYear: Eq<Date>
+```
+
+Added in v2.6.0
+
 # now
 
 Returns the number of milliseconds elapsed since January 1, 1970, 00:00:00 UTC
diff --git a/docs/modules/Either.ts.md b/docs/modules/Either.ts.md
index 63fc19ab..c76c1093 100644
--- a/docs/modules/Either.ts.md
+++ b/docs/modules/Either.ts.md
@@ -54,6 +54,7 @@ Added in v2.0.0
 - [bimap](#bimap)
 - [chain](#chain)
 - [chainFirst](#chainfirst)
+- [chainW](#chainw)
 - [duplicate](#duplicate)
 - [either](#either)
 - [elem](#elem)
@@ -70,6 +71,7 @@ Added in v2.0.0
 - [getApplySemigroup](#getapplysemigroup)
 - [getEq](#geteq)
 - [getOrElse](#getorelse)
+- [getOrElseW](#getorelsew)
 - [getSemigroup](#getsemigroup)
 - [getShow](#getshow)
 - [getValidation](#getvalidation)
@@ -219,6 +221,16 @@ export declare const chainFirst: <E, A, B>(f: (a: A) => Either<E, B>) => (ma: Ei
 
 Added in v2.0.0
 
+# chainW
+
+**Signature**
+
+```ts
+export declare const chainW: <D, A, B>(f: (a: A) => Either<D, B>) => <E>(ma: Either<E, A>) => Either<D | E, B>
+```
+
+Added in v2.6.0
+
 # duplicate
 
 **Signature**
@@ -457,6 +469,16 @@ export declare function getOrElse<E, A>(onLeft: (e: E) => A): (ma: Either<E, A>)
 
 Added in v2.0.0
 
+# getOrElseW
+
+**Signature**
+
+```ts
+export declare const getOrElseW: <E, B>(onLeft: (e: E) => B) => <A>(ma: Either<E, A>) => B | A
+```
+
+Added in v2.6.0
+
 # getSemigroup
 
 Semigroup returning the left-most non-`Left` value. If both operands are `Right`s then the inner values are
diff --git a/docs/modules/Eq.ts.md b/docs/modules/Eq.ts.md
index 258fe3db..c7056340 100644
--- a/docs/modules/Eq.ts.md
+++ b/docs/modules/Eq.ts.md
@@ -31,6 +31,7 @@ Added in v2.0.0
 - [eqStrict](#eqstrict)
 - [eqString](#eqstring)
 - [fromEquals](#fromequals)
+- [getMonoid](#getmonoid)
 - [getStructEq](#getstructeq)
 - [getTupleEq](#gettupleeq)
 - [~~strictEqual~~](#strictequal)
@@ -149,6 +150,16 @@ export declare function fromEquals<A>(equals: (x: A, y: A) => boolean): Eq<A>
 
 Added in v2.0.0
 
+# getMonoid
+
+**Signature**
+
+```ts
+export declare function getMonoid<A>(): Monoid<Eq<A>>
+```
+
+Added in v2.6.0
+
 # getStructEq
 
 **Signature**
diff --git a/docs/modules/IOEither.ts.md b/docs/modules/IOEither.ts.md
index fc29b609..150e547d 100644
--- a/docs/modules/IOEither.ts.md
+++ b/docs/modules/IOEither.ts.md
@@ -27,6 +27,7 @@ Added in v2.0.0
 - [chain](#chain)
 - [chainEitherK](#chaineitherk)
 - [chainFirst](#chainfirst)
+- [chainW](#chainw)
 - [filterOrElse](#filterorelse)
 - [flatten](#flatten)
 - [fold](#fold)
@@ -39,6 +40,7 @@ Added in v2.0.0
 - [getFilterable](#getfilterable)
 - [getIOValidation](#getiovalidation)
 - [getOrElse](#getorelse)
+- [getOrElseW](#getorelsew)
 - [getSemigroup](#getsemigroup)
 - [ioEither](#ioeither)
 - [left](#left)
@@ -182,6 +184,16 @@ export declare const chainFirst: <E, A, B>(f: (a: A) => IOEither<E, B>) => (ma:
 
 Added in v2.0.0
 
+# chainW
+
+**Signature**
+
+```ts
+export declare const chainW: <D, A, B>(f: (a: A) => IOEither<D, B>) => <E>(ma: IOEither<E, A>) => IOEither<D | E, B>
+```
+
+Added in v2.6.0
+
 # filterOrElse
 
 **Signature**
@@ -315,6 +327,16 @@ export declare function getOrElse<E, A>(onLeft: (e: E) => IO<A>): (ma: IOEither<
 
 Added in v2.0.0
 
+# getOrElseW
+
+**Signature**
+
+```ts
+export declare const getOrElseW: <E, B>(onLeft: (e: E) => IO<B>) => <A>(ma: IOEither<E, A>) => IO<B | A>
+```
+
+Added in v2.6.0
+
 # getSemigroup
 
 Semigroup returning the left-most non-`Left` value. If both operands are `Right`s then the inner values are
diff --git a/docs/modules/Option.ts.md b/docs/modules/Option.ts.md
index 33b24019..6781c7a9 100644
--- a/docs/modules/Option.ts.md
+++ b/docs/modules/Option.ts.md
@@ -71,6 +71,7 @@ Added in v2.0.0
 - [getLeft](#getleft)
 - [getMonoid](#getmonoid)
 - [getOrElse](#getorelse)
+- [getOrElseW](#getorelsew)
 - [getOrd](#getord)
 - [getRefinement](#getrefinement)
 - [getRight](#getright)
@@ -651,6 +652,16 @@ assert.strictEqual(
 
 Added in v2.0.0
 
+# getOrElseW
+
+**Signature**
+
+```ts
+export declare const getOrElseW: <B>(onNone: () => B) => <A>(ma: Option<A>) => B | A
+```
+
+Added in v2.6.0
+
 # getOrd
 
 The `Ord` instance allows `Option` values to be compared with
diff --git a/docs/modules/Reader.ts.md b/docs/modules/Reader.ts.md
index f1e4518c..337910aa 100644
--- a/docs/modules/Reader.ts.md
+++ b/docs/modules/Reader.ts.md
@@ -22,6 +22,7 @@ Added in v2.0.0
 - [asks](#asks)
 - [chain](#chain)
 - [chainFirst](#chainfirst)
+- [chainW](#chainw)
 - [compose](#compose)
 - [flatten](#flatten)
 - [getMonoid](#getmonoid)
@@ -140,6 +141,16 @@ export declare const chainFirst: <E, A, B>(f: (a: A) => Reader<E, B>) => (ma: Re
 
 Added in v2.0.0
 
+# chainW
+
+**Signature**
+
+```ts
+export declare const chainW: <Q, A, B>(f: (a: A) => Reader<Q, B>) => <R>(ma: Reader<R, A>) => Reader<R & Q, B>
+```
+
+Added in v2.6.0
+
 # compose
 
 **Signature**
diff --git a/docs/modules/ReaderEither.ts.md b/docs/modules/ReaderEither.ts.md
index 1fce07ef..6babf937 100644
--- a/docs/modules/ReaderEither.ts.md
+++ b/docs/modules/ReaderEither.ts.md
@@ -25,6 +25,7 @@ Added in v2.0.0
 - [chain](#chain)
 - [chainEitherK](#chaineitherk)
 - [chainFirst](#chainfirst)
+- [chainW](#chainw)
 - [filterOrElse](#filterorelse)
 - [flatten](#flatten)
 - [fold](#fold)
@@ -35,6 +36,7 @@ Added in v2.0.0
 - [getApplyMonoid](#getapplymonoid)
 - [getApplySemigroup](#getapplysemigroup)
 - [getOrElse](#getorelse)
+- [getOrElseW](#getorelsew)
 - [getReaderValidation](#getreadervalidation)
 - [getSemigroup](#getsemigroup)
 - [left](#left)
@@ -197,6 +199,18 @@ export declare const chainFirst: <R, E, A, B>(
 
 Added in v2.0.0
 
+# chainW
+
+**Signature**
+
+```ts
+export declare const chainW: <Q, D, A, B>(
+  f: (a: A) => ReaderEither<Q, D, B>
+) => <R, E>(ma: ReaderEither<R, E, A>) => ReaderEither<R & Q, D | E, B>
+```
+
+Added in v2.6.0
+
 # filterOrElse
 
 **Signature**
@@ -311,6 +325,18 @@ export declare function getOrElse<R, E, A>(onLeft: (e: E) => Reader<R, A>): (ma:
 
 Added in v2.0.0
 
+# getOrElseW
+
+**Signature**
+
+```ts
+export declare const getOrElseW: <Q, E, B>(
+  onLeft: (e: E) => Reader<Q, B>
+) => <R, A>(ma: ReaderEither<R, E, A>) => Reader<R & Q, B | A>
+```
+
+Added in v2.6.0
+
 # getReaderValidation
 
 **Signature**
diff --git a/docs/modules/ReaderTaskEither.ts.md b/docs/modules/ReaderTaskEither.ts.md
index 4f8f6223..29aefd16 100644
--- a/docs/modules/ReaderTaskEither.ts.md
+++ b/docs/modules/ReaderTaskEither.ts.md
@@ -28,6 +28,7 @@ Added in v2.0.0
 - [chainFirst](#chainfirst)
 - [chainIOEitherK](#chainioeitherk)
 - [chainTaskEitherK](#chaintaskeitherk)
+- [chainW](#chainw)
 - [filterOrElse](#filterorelse)
 - [flatten](#flatten)
 - [fold](#fold)
@@ -43,6 +44,7 @@ Added in v2.0.0
 - [getApplyMonoid](#getapplymonoid)
 - [getApplySemigroup](#getapplysemigroup)
 - [getOrElse](#getorelse)
+- [getOrElseW](#getorelsew)
 - [getReaderTaskValidation](#getreadertaskvalidation)
 - [getSemigroup](#getsemigroup)
 - [left](#left)
@@ -258,6 +260,18 @@ export declare function chainTaskEitherK<E, A, B>(
 
 Added in v2.4.0
 
+# chainW
+
+**Signature**
+
+```ts
+export declare const chainW: <Q, D, A, B>(
+  f: (a: A) => ReaderTaskEither<Q, D, B>
+) => <R, E>(ma: ReaderTaskEither<R, E, A>) => ReaderTaskEither<R & Q, D | E, B>
+```
+
+Added in v2.6.0
+
 # filterOrElse
 
 **Signature**
@@ -432,6 +446,18 @@ export declare function getOrElse<R, E, A>(
 
 Added in v2.0.0
 
+# getOrElseW
+
+**Signature**
+
+```ts
+export declare const getOrElseW: <Q, E, B>(
+  onLeft: (e: E) => ReaderTask<Q, B>
+) => <R, A>(ma: ReaderTaskEither<R, E, A>) => ReaderTask<R & Q, B | A>
+```
+
+Added in v2.6.0
+
 # getReaderTaskValidation
 
 **Signature**
diff --git a/docs/modules/StateReaderTaskEither.ts.md b/docs/modules/StateReaderTaskEither.ts.md
index 5cb922c3..8f2780d7 100644
--- a/docs/modules/StateReaderTaskEither.ts.md
+++ b/docs/modules/StateReaderTaskEither.ts.md
@@ -24,6 +24,7 @@ Added in v2.0.0
 - [chainIOEitherK](#chainioeitherk)
 - [chainReaderTaskEitherK](#chainreadertaskeitherk)
 - [chainTaskEitherK](#chaintaskeitherk)
+- [chainW](#chainw)
 - [evalState](#evalstate)
 - [execState](#execstate)
 - [filterOrElse](#filterorelse)
@@ -200,6 +201,18 @@ export declare function chainTaskEitherK<E, A, B>(
 
 Added in v2.4.0
 
+# chainW
+
+**Signature**
+
+```ts
+export declare const chainW: <S, Q, D, A, B>(
+  f: (a: A) => StateReaderTaskEither<S, Q, D, B>
+) => <R, E>(ma: StateReaderTaskEither<S, R, E, A>) => StateReaderTaskEither<S, R & Q, D | E, B>
+```
+
+Added in v2.6.0
+
 # evalState
 
 Run a computation in the `StateReaderTaskEither` monad, discarding the final state
diff --git a/docs/modules/TaskEither.ts.md b/docs/modules/TaskEither.ts.md
index 060dffd8..e4ca8952 100644
--- a/docs/modules/TaskEither.ts.md
+++ b/docs/modules/TaskEither.ts.md
@@ -28,6 +28,7 @@ Added in v2.0.0
 - [chainEitherK](#chaineitherk)
 - [chainFirst](#chainfirst)
 - [chainIOEitherK](#chainioeitherk)
+- [chainW](#chainw)
 - [filterOrElse](#filterorelse)
 - [flatten](#flatten)
 - [fold](#fold)
@@ -41,6 +42,7 @@ Added in v2.0.0
 - [getApplySemigroup](#getapplysemigroup)
 - [getFilterable](#getfilterable)
 - [getOrElse](#getorelse)
+- [getOrElseW](#getorelsew)
 - [getSemigroup](#getsemigroup)
 - [getTaskValidation](#gettaskvalidation)
 - [left](#left)
@@ -200,6 +202,18 @@ export declare function chainIOEitherK<E, A, B>(f: (a: A) => IOEither<E, B>): (m
 
 Added in v2.4.0
 
+# chainW
+
+**Signature**
+
+```ts
+export declare const chainW: <D, A, B>(
+  f: (a: A) => TaskEither<D, B>
+) => <E>(ma: TaskEither<E, A>) => TaskEither<D | E, B>
+```
+
+Added in v2.6.0
+
 # filterOrElse
 
 **Signature**
@@ -346,6 +360,16 @@ export declare function getOrElse<E, A>(onLeft: (e: E) => Task<A>): (ma: TaskEit
 
 Added in v2.0.0
 
+# getOrElseW
+
+**Signature**
+
+```ts
+export declare const getOrElseW: <E, B>(onLeft: (e: E) => Task<B>) => <A>(ma: TaskEither<E, A>) => Task<B | A>
+```
+
+Added in v2.6.0
+
 # getSemigroup
 
 Semigroup returning the left-most non-`Left` value. If both operands are `Right`s then the inner values are
diff --git a/docs/modules/Tree.ts.md b/docs/modules/Tree.ts.md
index 955937b7..34b2e413 100644
--- a/docs/modules/Tree.ts.md
+++ b/docs/modules/Tree.ts.md
@@ -33,6 +33,7 @@ Added in v2.0.0
 - [elem](#elem)
 - [extend](#extend)
 - [flatten](#flatten)
+- [fold](#fold)
 - [foldMap](#foldmap)
 - [getEq](#geteq)
 - [getShow](#getshow)
@@ -223,6 +224,41 @@ export declare const flatten: <A>(mma: Tree<Tree<A>>) => Tree<A>
 
 Added in v2.0.0
 
+# fold
+
+Fold a tree into a "summary" value in depth-first order.
+
+For each node in the tree, apply `f` to the `value` and the result of applying `f` to each `forest`.
+
+This is also known as the catamorphism on trees.
+
+**Signature**
+
+```ts
+export declare function fold<A, B>(f: (a: A, bs: Array<B>) => B): (tree: Tree<A>) => B
+```
+
+**Example**
+
+```ts
+import { fold, make } from 'fp-ts/lib/Tree'
+
+const t = make(1, [make(2), make(3)])
+
+const sum = (as: Array<number>) => as.reduce((a, acc) => a + acc, 0)
+
+// Sum the values in a tree:
+assert.deepStrictEqual(fold((a: number, bs: Array<number>) => a + sum(bs))(t), 6)
+
+// Find the maximum value in the tree:
+assert.deepStrictEqual(fold((a: number, bs: Array<number>) => bs.reduce((b, acc) => Math.max(b, acc), a))(t), 3)
+
+// Count the number of leaves in the tree:
+assert.deepStrictEqual(fold((_: number, bs: Array<number>) => (bs.length === 0 ? 1 : sum(bs)))(t), 2)
+```
+
+Added in v2.6.0
+
 # foldMap
 
 **Signature**
diff --git a/dtslint/ts3.5/Either.ts b/dtslint/ts3.5/Either.ts
new file mode 100644
index 00000000..7f5ac524
--- /dev/null
+++ b/dtslint/ts3.5/Either.ts
@@ -0,0 +1,22 @@
+import * as _ from '../../src/Either'
+import { pipe } from '../../src/pipeable'
+
+//
+// getOrElseW
+//
+
+// $ExpectType string | null
+pipe(
+  _.right('a'),
+  _.getOrElseW(() => null)
+)
+
+//
+// chainW
+//
+
+// $ExpectType Either<string | number, number>
+pipe(
+  _.right<string, string>('a'),
+  _.chainW(() => _.right<number, number>(1))
+)
diff --git a/dtslint/ts3.5/IOEither.ts b/dtslint/ts3.5/IOEither.ts
new file mode 100644
index 00000000..25aa3f08
--- /dev/null
+++ b/dtslint/ts3.5/IOEither.ts
@@ -0,0 +1,23 @@
+import * as _ from '../../src/IOEither'
+import * as IO from '../../src/IO'
+import { pipe } from '../../src/pipeable'
+
+//
+// getOrElseW
+//
+
+// $ExpectType IO<string | null>
+pipe(
+  _.right('a'),
+  _.getOrElseW(() => IO.of(null))
+)
+
+//
+// chainW
+//
+
+// $ExpectType IOEither<string | number, number>
+pipe(
+  _.right<string, string>('a'),
+  _.chainW(() => _.right<number, number>(1))
+)
diff --git a/dtslint/ts3.5/Option.ts b/dtslint/ts3.5/Option.ts
new file mode 100644
index 00000000..81e42b8a
--- /dev/null
+++ b/dtslint/ts3.5/Option.ts
@@ -0,0 +1,12 @@
+import * as _ from '../../src/Option'
+import { pipe } from '../../src/pipeable'
+
+//
+// getOrElseW
+//
+
+// $ExpectType string | null
+pipe(
+  _.some('a'),
+  _.getOrElseW(() => null)
+)
diff --git a/dtslint/ts3.5/Reader.ts b/dtslint/ts3.5/Reader.ts
new file mode 100644
index 00000000..8497fb5f
--- /dev/null
+++ b/dtslint/ts3.5/Reader.ts
@@ -0,0 +1,12 @@
+import * as _ from '../../src/Reader'
+import { pipe } from '../../src/pipeable'
+
+//
+// chainW
+//
+
+// $ExpectType Reader<{ a: string; } & { b: number; }, number>
+pipe(
+  _.of<{ a: string }, string>('a'),
+  _.chainW(() => _.of<{ b: number }, number>(1))
+)
diff --git a/dtslint/ts3.5/ReaderEither.ts b/dtslint/ts3.5/ReaderEither.ts
new file mode 100644
index 00000000..5acdf57f
--- /dev/null
+++ b/dtslint/ts3.5/ReaderEither.ts
@@ -0,0 +1,23 @@
+import * as _ from '../../src/ReaderEither'
+import * as R from '../../src/Reader'
+import { pipe } from '../../src/pipeable'
+
+//
+// getOrElseW
+//
+
+// $ExpectType Reader<{ a: string; } & { b: number; }, string | null>
+pipe(
+  _.right<{ a: string }, string, string>('a'),
+  _.getOrElseW(() => R.of<{ b: number }, null>(null))
+)
+
+//
+// chainW
+//
+
+// $ExpectType ReaderEither<{ a: string; } & { b: number; }, string | number, number>
+pipe(
+  _.right<{ a: string }, string, string>('a'),
+  _.chainW(() => _.right<{ b: number }, number, number>(1))
+)
diff --git a/dtslint/ts3.5/ReaderTaskEither.ts b/dtslint/ts3.5/ReaderTaskEither.ts
new file mode 100644
index 00000000..78298d06
--- /dev/null
+++ b/dtslint/ts3.5/ReaderTaskEither.ts
@@ -0,0 +1,23 @@
+import * as _ from '../../src/ReaderTaskEither'
+import * as RT from '../../src/ReaderTask'
+import { pipe } from '../../src/pipeable'
+
+//
+// getOrElseW
+//
+
+// $ExpectType ReaderTask<{ a: string; } & { b: number; }, string | null>
+pipe(
+  _.right<{ a: string }, string, string>('a'),
+  _.getOrElseW(() => RT.of<{ b: number }, null>(null))
+)
+
+//
+// chainW
+//
+
+// $ExpectType ReaderTaskEither<{ a: string; } & { b: number; }, string | number, number>
+pipe(
+  _.right<{ a: string }, string, string>('a'),
+  _.chainW(() => _.right<{ b: number }, number, number>(1))
+)
diff --git a/dtslint/ts3.5/StateReaderTaskEither.ts b/dtslint/ts3.5/StateReaderTaskEither.ts
new file mode 100644
index 00000000..1f10d931
--- /dev/null
+++ b/dtslint/ts3.5/StateReaderTaskEither.ts
@@ -0,0 +1,12 @@
+import * as _ from '../../src/StateReaderTaskEither'
+import { pipe } from '../../src/pipeable'
+
+//
+// chainW
+//
+
+// $ExpectType StateReaderTaskEither<boolean, { a: string; } & { b: number; }, string | number, number>
+pipe(
+  _.right<boolean, { a: string }, string, string>('a'),
+  _.chainW(() => _.right<boolean, { b: number }, number, number>(1))
+)
diff --git a/dtslint/ts3.5/TaskEither.ts b/dtslint/ts3.5/TaskEither.ts
new file mode 100644
index 00000000..3fc6d718
--- /dev/null
+++ b/dtslint/ts3.5/TaskEither.ts
@@ -0,0 +1,23 @@
+import * as _ from '../../src/TaskEither'
+import * as T from '../../src/Task'
+import { pipe } from '../../src/pipeable'
+
+//
+// getOrElseW
+//
+
+// $ExpectType Task<string | null>
+pipe(
+  _.right('a'),
+  _.getOrElseW(() => T.of(null))
+)
+
+//
+// chainW
+//
+
+// $ExpectType TaskEither<string | number, number>
+pipe(
+  _.right<string, string>('a'),
+  _.chainW(() => _.right<number, number>(1))
+)
diff --git a/package.json b/package.json
index 12bb92bd..00649a58 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "fp-ts",
-  "version": "2.5.4",
+  "version": "2.6.0",
   "description": "Functional programming in TypeScript",
   "files": [
     "lib",
diff --git a/src/Const.ts b/src/Const.ts
index a1166a45..125a7f95 100644
--- a/src/Const.ts
+++ b/src/Const.ts
@@ -1,16 +1,29 @@
 /**
+ * The `Const` type constructor, which wraps its first type argument and ignores its second.
+ * That is, `Const<E, A>` is isomorphic to `E` for any `A`.
+ *
+ * `Const` has some useful instances. For example, the `Applicative` instance allows us to collect results using a `Monoid`
+ * while ignoring return values.
+ *
  * @since 2.0.0
  */
 import { Applicative2C } from './Applicative'
 import { Apply2C } from './Apply'
+import { Bifunctor2 } from './Bifunctor'
+import { BooleanAlgebra } from './BooleanAlgebra'
+import { Bounded } from './Bounded'
 import { Contravariant2 } from './Contravariant'
-import { unsafeCoerce, identity } from './function'
+import { Eq } from './Eq'
+import { identity, unsafeCoerce } from './function'
 import { Functor2 } from './Functor'
+import { HeytingAlgebra } from './HeytingAlgebra'
 import { Monoid } from './Monoid'
+import { Ord } from './Ord'
+import { pipeable } from './pipeable'
+import { Ring } from './Ring'
 import { Semigroup } from './Semigroup'
-import { Eq } from './Eq'
+import { Semiring } from './Semiring'
 import { Show } from './Show'
-import { pipeable } from './pipeable'
 
 declare module './HKT' {
   interface URItoKind2<E, A> {
@@ -52,6 +65,46 @@ export function getShow<E, A>(S: Show<E>): Show<Const<E, A>> {
  */
 export const getEq: <E, A>(E: Eq<E>) => Eq<Const<E, A>> = identity
 
+/**
+ * @since 2.6.0
+ */
+export const getOrd: <E, A>(O: Ord<E>) => Ord<Const<E, A>> = identity
+
+/**
+ * @since 2.6.0
+ */
+export const getBounded: <E, A>(B: Bounded<E>) => Bounded<Const<E, A>> = identity as any
+
+/**
+ * @since 2.6.0
+ */
+export const getSemigroup: <E, A>(S: Semigroup<E>) => Semigroup<Const<E, A>> = identity as any
+
+/**
+ * @since 2.6.0
+ */
+export const getMonoid: <E, A>(M: Monoid<E>) => Monoid<Const<E, A>> = identity as any
+
+/**
+ * @since 2.6.0
+ */
+export const getSemiring: <E, A>(S: Semiring<E>) => Semiring<Const<E, A>> = identity as any
+
+/**
+ * @since 2.6.0
+ */
+export const getRing: <E, A>(S: Ring<E>) => Ring<Const<E, A>> = identity as any
+
+/**
+ * @since 2.6.0
+ */
+export const getHeytingAlgebra: <E, A>(H: HeytingAlgebra<E>) => HeytingAlgebra<Const<E, A>> = identity as any
+
+/**
+ * @since 2.6.0
+ */
+export const getBooleanAlgebra: <E, A>(H: BooleanAlgebra<E>) => BooleanAlgebra<Const<E, A>> = identity as any
+
 /**
  * @since 2.0.0
  */
@@ -77,10 +130,12 @@ export function getApplicative<E>(M: Monoid<E>): Applicative2C<URI, E> {
 /**
  * @since 2.0.0
  */
-export const const_: Functor2<URI> & Contravariant2<URI> = {
+export const const_: Functor2<URI> & Contravariant2<URI> & Bifunctor2<URI> = {
   URI,
   map: unsafeCoerce,
-  contramap: unsafeCoerce
+  contramap: unsafeCoerce,
+  bimap: (fea, f) => make(f(fea)),
+  mapLeft: (fea, f) => make(f(fea))
 }
 
 const { contramap, map } = pipeable(const_)
diff --git a/src/Date.ts b/src/Date.ts
index df302f37..bfe40c11 100644
--- a/src/Date.ts
+++ b/src/Date.ts
@@ -2,6 +2,7 @@
  * @since 2.0.0
  */
 import { IO } from './IO'
+import { eqNumber, contramap, Eq } from './Eq'
 
 /**
  * Returns the current `Date`
@@ -16,3 +17,18 @@ export const create: IO<Date> = () => new Date()
  * @since 2.0.0
  */
 export const now: IO<number> = () => new Date().getTime()
+
+/**
+ * @since 2.6.0
+ */
+export const eqDate: Eq<Date> = contramap((x: Date) => x.getDate())(eqNumber)
+
+/**
+ * @since 2.6.0
+ */
+export const eqMonth: Eq<Date> = contramap((x: Date) => x.getMonth())(eqNumber)
+
+/**
+ * @since 2.6.0
+ */
+export const eqYear: Eq<Date> = contramap((x: Date) => x.getFullYear())(eqNumber)
diff --git a/src/Either.ts b/src/Either.ts
index 4a39ffd5..866e3e7f 100644
--- a/src/Either.ts
+++ b/src/Either.ts
@@ -323,6 +323,11 @@ export function getOrElse<E, A>(onLeft: (e: E) => A): (ma: Either<E, A>) => A {
   return (ma) => (isLeft(ma) ? onLeft(ma.left) : ma.right)
 }
 
+/**
+ * @since 2.6.0
+ */
+export const getOrElseW: <E, B>(onLeft: (e: E) => B) => <A>(ma: Either<E, A>) => A | B = getOrElse as any
+
 /**
  * @since 2.0.0
  */
@@ -591,6 +596,11 @@ const {
   filterOrElse
 } = pipeable(either)
 
+/**
+ * @since 2.6.0
+ */
+export const chainW: <D, A, B>(f: (a: A) => Either<D, B>) => <E>(ma: Either<E, A>) => Either<E | D, B> = chain as any
+
 export {
   /**
    * @since 2.0.0
diff --git a/src/Eq.ts b/src/Eq.ts
index bb263b07..951534c4 100644
--- a/src/Eq.ts
+++ b/src/Eq.ts
@@ -12,6 +12,7 @@
 import { Contravariant1 } from './Contravariant'
 import { pipeable } from './pipeable'
 import { ReadonlyRecord } from './ReadonlyRecord'
+import { Monoid } from './Monoid'
 
 declare module './HKT' {
   interface URItoKind<A> {
@@ -133,3 +134,17 @@ export {
  * @since 2.0.0
  */
 export const eqDate: Eq<Date> = eq.contramap(eqNumber, (date) => date.valueOf())
+
+const empty: Eq<unknown> = {
+  equals: () => true
+}
+
+/**
+ * @since 2.6.0
+ */
+export function getMonoid<A>(): Monoid<Eq<A>> {
+  return {
+    concat: (x, y) => fromEquals((a, b) => x.equals(a, b) && y.equals(a, b)),
+    empty
+  }
+}
diff --git a/src/IOEither.ts b/src/IOEither.ts
index 85467d80..fb264ade 100644
--- a/src/IOEither.ts
+++ b/src/IOEither.ts
@@ -78,6 +78,11 @@ export function getOrElse<E, A>(onLeft: (e: E) => IO<A>): (ma: IOEither<E, A>) =
   return (ma) => T.getOrElse(ma, onLeft)
 }
 
+/**
+ * @since 2.6.0
+ */
+export const getOrElseW: <E, B>(onLeft: (e: E) => IO<B>) => <A>(ma: IOEither<E, A>) => IO<A | B> = getOrElse as any
+
 /**
  * @since 2.0.0
  */
@@ -225,6 +230,13 @@ const {
   filterOrElse
 } = pipeable(ioEither)
 
+/**
+ * @since 2.6.0
+ */
+export const chainW: <D, A, B>(
+  f: (a: A) => IOEither<D, B>
+) => <E>(ma: IOEither<E, A>) => IOEither<E | D, B> = chain as any
+
 export {
   /**
    * @since 2.0.0
diff --git a/src/Option.ts b/src/Option.ts
index 93a7811c..3225cfc8 100644
--- a/src/Option.ts
+++ b/src/Option.ts
@@ -257,6 +257,11 @@ export function getOrElse<A>(onNone: () => A): (ma: Option<A>) => A {
   return (ma) => (isNone(ma) ? onNone() : ma.value)
 }
 
+/**
+ * @since 2.6.0
+ */
+export const getOrElseW: <B>(onNone: () => B) => <A>(ma: Option<A>) => A | B = getOrElse as any
+
 /**
  * Returns `true` if `ma` contains `a`
  *
diff --git a/src/Reader.ts b/src/Reader.ts
index 9759878c..adc33fe3 100644
--- a/src/Reader.ts
+++ b/src/Reader.ts
@@ -109,6 +109,11 @@ export const reader: Monad2<URI> & Profunctor2<URI> & Category2<URI> & Strong2<U
 
 const { ap, apFirst, apSecond, chain, chainFirst, compose, flatten, map, promap } = pipeable(reader)
 
+/**
+ * @since 2.6.0
+ */
+export const chainW: <Q, A, B>(f: (a: A) => Reader<Q, B>) => <R>(ma: Reader<R, A>) => Reader<R & Q, B> = chain as any
+
 export {
   /**
    * @since 2.0.0
diff --git a/src/ReaderEither.ts b/src/ReaderEither.ts
index 5bbe92df..797361d8 100644
--- a/src/ReaderEither.ts
+++ b/src/ReaderEither.ts
@@ -75,6 +75,13 @@ export function getOrElse<R, E, A>(onLeft: (e: E) => Reader<R, A>): (ma: ReaderE
   return (ma) => T.getOrElse(ma, onLeft)
 }
 
+/**
+ * @since 2.6.0
+ */
+export const getOrElseW: <Q, E, B>(
+  onLeft: (e: E) => Reader<Q, B>
+) => <R, A>(ma: ReaderEither<R, E, A>) => Reader<R & Q, A | B> = getOrElse as any
+
 /**
  * @since 2.0.0
  */
@@ -204,6 +211,13 @@ const {
   filterOrElse
 } = pipeable(readerEither)
 
+/**
+ * @since 2.6.0
+ */
+export const chainW: <Q, D, A, B>(
+  f: (a: A) => ReaderEither<Q, D, B>
+) => <R, E>(ma: ReaderEither<R, E, A>) => ReaderEither<R & Q, E | D, B> = chain as any
+
 export {
   /**
    * @since 2.0.0
diff --git a/src/ReaderTaskEither.ts b/src/ReaderTaskEither.ts
index c793b88c..f9c94f38 100644
--- a/src/ReaderTaskEither.ts
+++ b/src/ReaderTaskEither.ts
@@ -165,6 +165,13 @@ export function getOrElse<R, E, A>(
   return (ma) => (r) => TE.getOrElse<E, A>((e) => onLeft(e)(r))(ma(r))
 }
 
+/**
+ * @since 2.6.0
+ */
+export const getOrElseW: <Q, E, B>(
+  onLeft: (e: E) => ReaderTask<Q, B>
+) => <R, A>(ma: ReaderTaskEither<R, E, A>) => ReaderTask<R & Q, A | B> = getOrElse as any
+
 /**
  * @since 2.0.0
  */
@@ -360,6 +367,13 @@ const {
   filterOrElse
 } = pipeable(readerTaskEither)
 
+/**
+ * @since 2.6.0
+ */
+export const chainW: <Q, D, A, B>(
+  f: (a: A) => ReaderTaskEither<Q, D, B>
+) => <R, E>(ma: ReaderTaskEither<R, E, A>) => ReaderTaskEither<R & Q, E | D, B> = chain as any
+
 export {
   /**
    * @since 2.0.0
diff --git a/src/StateReaderTaskEither.ts b/src/StateReaderTaskEither.ts
index 117216fc..e2203c92 100644
--- a/src/StateReaderTaskEither.ts
+++ b/src/StateReaderTaskEither.ts
@@ -306,6 +306,13 @@ const {
   fromPredicate
 } = pipeable(stateReaderTaskEither)
 
+/**
+ * @since 2.6.0
+ */
+export const chainW: <S, Q, D, A, B>(
+  f: (a: A) => StateReaderTaskEither<S, Q, D, B>
+) => <R, E>(ma: StateReaderTaskEither<S, R, E, A>) => StateReaderTaskEither<S, R & Q, E | D, B> = chain as any
+
 export {
   /**
    * @since 2.0.0
diff --git a/src/TaskEither.ts b/src/TaskEither.ts
index d611cdd5..56d17492 100644
--- a/src/TaskEither.ts
+++ b/src/TaskEither.ts
@@ -102,6 +102,13 @@ export function getOrElse<E, A>(onLeft: (e: E) => Task<A>): (ma: TaskEither<E, A
   return (ma) => T.getOrElse(ma, onLeft)
 }
 
+/**
+ * @since 2.6.0
+ */
+export const getOrElseW: <E, B>(
+  onLeft: (e: E) => Task<B>
+) => <A>(ma: TaskEither<E, A>) => Task<A | B> = getOrElse as any
+
 /**
  * @since 2.0.0
  */
@@ -355,6 +362,13 @@ const {
   filterOrElse
 } = pipeable(taskEither)
 
+/**
+ * @since 2.6.0
+ */
+export const chainW: <D, A, B>(
+  f: (a: A) => TaskEither<D, B>
+) => <E>(ma: TaskEither<E, A>) => TaskEither<E | D, B> = chain as any
+
 export {
   /**
    * @since 2.0.0
diff --git a/src/Tree.ts b/src/Tree.ts
index 1934b4c4..72967f0c 100644
--- a/src/Tree.ts
+++ b/src/Tree.ts
@@ -220,6 +220,36 @@ export function elem<A>(E: Eq<A>): (a: A, fa: Tree<A>) => boolean {
   return go
 }
 
+/**
+ * Fold a tree into a "summary" value in depth-first order.
+ *
+ * For each node in the tree, apply `f` to the `value` and the result of applying `f` to each `forest`.
+ *
+ * This is also known as the catamorphism on trees.
+ *
+ * @example
+ * import { fold, make } from 'fp-ts/lib/Tree'
+ *
+ * const t = make(1, [make(2), make(3)])
+ *
+ * const sum = (as: Array<number>) => as.reduce((a, acc) => a + acc, 0)
+ *
+ * // Sum the values in a tree:
+ * assert.deepStrictEqual(fold((a: number, bs: Array<number>) => a + sum(bs))(t), 6)
+ *
+ * // Find the maximum value in the tree:
+ * assert.deepStrictEqual(fold((a: number, bs: Array<number>) => bs.reduce((b, acc) => Math.max(b, acc), a))(t), 3)
+ *
+ * // Count the number of leaves in the tree:
+ * assert.deepStrictEqual(fold((_: number, bs: Array<number>) => (bs.length === 0 ? 1 : sum(bs)))(t), 2)
+ *
+ * @since 2.6.0
+ */
+export function fold<A, B>(f: (a: A, bs: Array<B>) => B): (tree: Tree<A>) => B {
+  const go = (tree: Tree<A>): B => f(tree.value, tree.forest.map(go))
+  return go
+}
+
 /**
  * @since 2.0.0
  */


Test patch:
diff --git a/test/Const.ts b/test/Const.ts
index 8b7061df..d2ebe1df 100644
--- a/test/Const.ts
+++ b/test/Const.ts
@@ -18,6 +18,19 @@ describe('Const', () => {
     assert.deepStrictEqual(const_.contramap(fa, double), fa)
   })
 
+  it('bimap', () => {
+    const fa: Const<string, number> = make('a')
+    const f = (s: string): string => s.toUpperCase()
+    const g = (n: number): number => n * 2
+    assert.deepStrictEqual(const_.bimap(fa, f, g), make('A'))
+  })
+
+  it('mapLeft', () => {
+    const fa: Const<string, number> = make('a')
+    const f = (s: string): string => s.toUpperCase()
+    assert.deepStrictEqual(const_.mapLeft(fa, f), make('A'))
+  })
+
   it('getApplicative', () => {
     const F = getApplicative(monoidString)
     assert.deepStrictEqual(F.of(1), make(''))
diff --git a/test/Date.ts b/test/Date.ts
index d3a170ae..d2df6b37 100644
--- a/test/Date.ts
+++ b/test/Date.ts
@@ -1,17 +1,32 @@
 import * as assert from 'assert'
-import { create, now } from '../src/Date'
+import * as _ from '../src/Date'
 
 describe('Date', () => {
   it('create', () => {
-    const d1 = create()
+    const d1 = _.create()
     const m2 = new Date().getTime()
     assert.deepStrictEqual(d1 instanceof Date, true)
     assert.deepStrictEqual(d1.getTime(), m2)
   })
 
   it('now', () => {
-    const m1 = now()
+    const m1 = _.now()
     const m2 = new Date().getTime()
     assert.deepStrictEqual(m1, m2)
   })
+
+  it('eqDate', () => {
+    assert.deepStrictEqual(_.eqDate.equals(new Date(2000, 10, 1), new Date(2000, 11, 1)), true)
+    assert.deepStrictEqual(_.eqDate.equals(new Date(2000, 10, 1), new Date(2000, 11, 2)), false)
+  })
+
+  it('eqMonth', () => {
+    assert.deepStrictEqual(_.eqMonth.equals(new Date(2000, 10, 1), new Date(2000, 10, 7)), true)
+    assert.deepStrictEqual(_.eqMonth.equals(new Date(2000, 10, 1), new Date(2000, 11, 7)), false)
+  })
+
+  it('eqYear', () => {
+    assert.deepStrictEqual(_.eqYear.equals(new Date(2000, 10, 1), new Date(2000, 11, 7)), true)
+    assert.deepStrictEqual(_.eqYear.equals(new Date(2000, 10, 1), new Date(2001, 11, 7)), false)
+  })
 })
diff --git a/test/Eq.ts b/test/Eq.ts
index 10df2046..4afbda34 100644
--- a/test/Eq.ts
+++ b/test/Eq.ts
@@ -1,9 +1,10 @@
 import * as assert from 'assert'
-import { eq, eqDate, eqNumber, eqString, fromEquals, getTupleEq, eqBoolean, getStructEq, eqStrict } from '../src/Eq'
+import * as _ from '../src/Eq'
+import { fold } from '../src/Monoid'
 
 describe('Eq', () => {
   it('getTupleEq', () => {
-    const S = getTupleEq(eqString, eqNumber, eqBoolean)
+    const S = _.getTupleEq(_.eqString, _.eqNumber, _.eqBoolean)
     assert.deepStrictEqual(S.equals(['a', 1, true], ['a', 1, true]), true)
     assert.deepStrictEqual(S.equals(['a', 1, true], ['b', 1, true]), false)
     assert.deepStrictEqual(S.equals(['a', 1, true], ['a', 2, true]), false)
@@ -19,7 +20,7 @@ describe('Eq', () => {
       readonly x: number
     }
     let nbCall = 0
-    const S1 = fromEquals<A>((a, b) => {
+    const S1 = _.fromEquals<A>((a, b) => {
       nbCall += 1
       return a.x === b.x
     })
@@ -32,7 +33,7 @@ describe('Eq', () => {
   })
 
   it('contramap', () => {
-    const S = eq.contramap(eqString, (p: Person) => p.name)
+    const S = _.eq.contramap(_.eqString, (p: Person) => p.name)
     assert.deepStrictEqual(S.equals({ name: 'a', age: 1 }, { name: 'a', age: 2 }), true)
     assert.deepStrictEqual(S.equals({ name: 'a', age: 1 }, { name: 'a', age: 1 }), true)
     assert.deepStrictEqual(S.equals({ name: 'a', age: 1 }, { name: 'b', age: 1 }), false)
@@ -40,15 +41,15 @@ describe('Eq', () => {
   })
 
   it('eqDate', () => {
-    assert.deepStrictEqual(eqDate.equals(new Date(0), new Date(0)), true)
-    assert.deepStrictEqual(eqDate.equals(new Date(0), new Date(1)), false)
-    assert.deepStrictEqual(eqDate.equals(new Date(1), new Date(0)), false)
+    assert.deepStrictEqual(_.eqDate.equals(new Date(0), new Date(0)), true)
+    assert.deepStrictEqual(_.eqDate.equals(new Date(0), new Date(1)), false)
+    assert.deepStrictEqual(_.eqDate.equals(new Date(1), new Date(0)), false)
   })
 
   it('getStructEq', () => {
-    const S = getStructEq<Person>({
-      name: eqString,
-      age: eqNumber
+    const S = _.getStructEq<Person>({
+      name: _.eqString,
+      age: _.eqNumber
     })
     assert.deepStrictEqual(S.equals({ name: 'a', age: 1 }, { name: 'a', age: 1 }), true)
     assert.deepStrictEqual(S.equals({ name: 'a', age: 1 }, { name: 'a', age: 2 }), false)
@@ -56,7 +57,20 @@ describe('Eq', () => {
   })
 
   it('eqStrict', () => {
-    assert.deepStrictEqual(eqStrict.equals(1, 1), true)
-    assert.deepStrictEqual(eqStrict.equals(1, 'a'), false)
+    assert.deepStrictEqual(_.eqStrict.equals(1, 1), true)
+    assert.deepStrictEqual(_.eqStrict.equals(1, 'a'), false)
+  })
+
+  it('getMonoid', () => {
+    type T = readonly [string, number, boolean]
+    const M = _.getMonoid<T>()
+    const eqFst: _.Eq<T> = _.contramap((x: T) => x[0])(_.eqString)
+    const eqSnd: _.Eq<T> = _.contramap((x: T) => x[1])(_.eqNumber)
+    const eq3rd: _.Eq<T> = _.contramap((x: T) => x[2])(_.eqBoolean)
+    const eq = fold(M)([eqFst, eqSnd, eq3rd])
+    assert.deepStrictEqual(eq.equals(['a', 1, true], ['a', 1, true]), true)
+    assert.deepStrictEqual(eq.equals(['a', 1, true], ['b', 1, true]), false)
+    assert.deepStrictEqual(eq.equals(['a', 1, true], ['a', 2, true]), false)
+    assert.deepStrictEqual(eq.equals(['a', 1, true], ['a', 1, false]), false)
   })
 })
diff --git a/test/Tree.ts b/test/Tree.ts
index d84125d8..ebbd387f 100644
--- a/test/Tree.ts
+++ b/test/Tree.ts
@@ -4,7 +4,7 @@ import * as I from '../src/Identity'
 import { monoidString } from '../src/Monoid'
 import { eq, Eq, eqNumber } from '../src/Eq'
 import { showString } from '../src/Show'
-import { drawTree, elem, getEq, getShow, Tree, tree, unfoldTree, unfoldTreeM, make } from '../src/Tree'
+import { drawTree, elem, getEq, getShow, Tree, tree, unfoldTree, unfoldTreeM, make, fold } from '../src/Tree'
 
 describe('Tree', () => {
   it('map', () => {
@@ -165,4 +165,12 @@ describe('Tree', () => {
     const t2 = make('a', [make('b'), make('c')])
     assert.deepStrictEqual(S.show(t2), `make("a", [make("b"), make("c")])`)
   })
+
+  it('fold', () => {
+    const t = make(1, [make(2), make(3)])
+    assert.deepStrictEqual(
+      fold((a: number, bs: ReadonlyArray<number>) => bs.reduce((b, acc) => Math.max(b, acc), a))(t),
+      3
+    )
+  })
 })