diff --git a/docs/modules/Monoid.ts.md b/docs/modules/Monoid.ts.md
index 1c80e2f5a..3dbbf9dc5 100644
--- a/docs/modules/Monoid.ts.md
+++ b/docs/modules/Monoid.ts.md
@@ -6,6 +6,37 @@ parent: Modules
## Monoid overview
+`Monoid` extends the power of `Semigroup` by providing an additional `empty` value.
+
+```ts
+interface Semigroup {
+ readonly concat: (x: A, y: A) => A
+}
+
+interface Monoid extends Semigroup {
+ readonly empty: A
+}
+```
+
+This `empty` value should be an identity for the `concat` operation, which means the following equalities hold for any choice of `x`.
+
+```ts
+concat(x, empty) = concat(empty, x) = x
+```
+
+Many types that form a `Semigroup` also form a `Monoid`, such as `number`s (with `0`) and `string`s (with `''`).
+
+```ts
+import { Monoid } from 'fp-ts/Monoid'
+
+const monoidString: Monoid = {
+ concat: (x, y) => x + y,
+ empty: '',
+}
+```
+
+_Adapted from https://typelevel.org/cats_
+
Added in v2.0.0
---
@@ -60,6 +91,8 @@ Added in v2.0.0
## getEndomorphismMonoid
+Endomorphism form a monoid where the `empty` value is the identity function.
+
**Signature**
```ts
@@ -70,36 +103,90 @@ Added in v2.0.0
## getFunctionMonoid
+Unary functions form a monoid as long as you can provide a monoid for the codomain.
+
**Signature**
```ts
export declare function getFunctionMonoid(M: Monoid): () => Monoid<(a: A) => M>
```
+**Example**
+
+```ts
+import { Predicate } from 'fp-ts/function'
+import * as M from 'fp-ts/Monoid'
+
+const f: Predicate = (n) => n <= 2
+const g: Predicate = (n) => n >= 0
+
+const M1 = M.getFunctionMonoid(M.monoidAll)()
+
+assert.deepStrictEqual(M1.concat(f, g)(1), true)
+assert.deepStrictEqual(M1.concat(f, g)(3), false)
+
+const M2 = M.getFunctionMonoid(M.monoidAny)()
+
+assert.deepStrictEqual(M2.concat(f, g)(1), true)
+assert.deepStrictEqual(M2.concat(f, g)(3), true)
+```
+
Added in v2.0.0
## getJoinMonoid
+Get a monoid where `concat` will return the maximum, based on the provided bounded order.
+
+The `empty` value is the `bottom` value.
+
**Signature**
```ts
export declare function getJoinMonoid(B: Bounded): Monoid
```
+**Example**
+
+```ts
+import * as B from 'fp-ts/Bounded'
+import * as M from 'fp-ts/Monoid'
+
+const M1 = M.getJoinMonoid(B.boundedNumber)
+
+assert.deepStrictEqual(M1.concat(1, 2), 2)
+```
+
Added in v2.0.0
## getMeetMonoid
+Get a monoid where `concat` will return the minimum, based on the provided bounded order.
+
+The `empty` value is the `top` value.
+
**Signature**
```ts
export declare function getMeetMonoid(B: Bounded): Monoid
```
+**Example**
+
+```ts
+import * as B from 'fp-ts/Bounded'
+import * as M from 'fp-ts/Monoid'
+
+const M1 = M.getMeetMonoid(B.boundedNumber)
+
+assert.deepStrictEqual(M1.concat(1, 2), 1)
+```
+
Added in v2.0.0
## getStructMonoid
+Given a struct of monoids returns a monoid for the struct.
+
**Signature**
```ts
@@ -108,6 +195,24 @@ export declare function getStructMonoid>(
): Monoid
```
+**Example**
+
+```ts
+import * as M from 'fp-ts/Monoid'
+
+interface Point {
+ readonly x: number
+ readonly y: number
+}
+
+const monoidPoint = M.getStructMonoid({
+ x: M.monoidSum,
+ y: M.monoidSum,
+})
+
+assert.deepStrictEqual(monoidPoint.concat({ x: 1, y: 2 }, { x: 3, y: 4 }), { x: 4, y: 6 })
+```
+
Added in v2.0.0
## getTupleMonoid
@@ -119,7 +224,7 @@ Given a tuple of monoids returns a monoid for the tuple
```ts
export declare function getTupleMonoid>>(
...monoids: T
-): Monoid<{ [K in keyof T]: T[K] extends Semigroup ? A : never }>
+): Monoid<{ [K in keyof T]: T[K] extends S.Semigroup ? A : never }>
```
**Example**
@@ -138,7 +243,9 @@ Added in v2.0.0
## monoidAll
-Boolean monoid under conjunction
+`boolean` monoid under conjunction.
+
+The `empty` value is `true`.
**Signature**
@@ -146,11 +253,22 @@ Boolean monoid under conjunction
export declare const monoidAll: Monoid
```
+**Example**
+
+```ts
+import * as M from 'fp-ts/Monoid'
+
+assert.deepStrictEqual(M.monoidAll.concat(true, true), true)
+assert.deepStrictEqual(M.monoidAll.concat(true, false), false)
+```
+
Added in v2.0.0
## monoidAny
-Boolean monoid under disjunction
+`boolean` monoid under disjunction.
+
+The `empty` value is `false`.
**Signature**
@@ -158,11 +276,23 @@ Boolean monoid under disjunction
export declare const monoidAny: Monoid
```
+**Example**
+
+```ts
+import * as M from 'fp-ts/Monoid'
+
+assert.deepStrictEqual(M.monoidAny.concat(true, true), true)
+assert.deepStrictEqual(M.monoidAny.concat(true, false), true)
+assert.deepStrictEqual(M.monoidAny.concat(false, false), false)
+```
+
Added in v2.0.0
## monoidProduct
-Number monoid under multiplication
+`number` monoid under multiplication.
+
+The `empty` value is `1`.
**Signature**
@@ -170,21 +300,43 @@ Number monoid under multiplication
export declare const monoidProduct: Monoid
```
+**Example**
+
+```ts
+import * as M from 'fp-ts/Monoid'
+
+assert.deepStrictEqual(M.monoidProduct.concat(2, 3), 6)
+```
+
Added in v2.0.0
## monoidString
+`string` monoid under concatenation.
+
+The `empty` value is `''`.
+
**Signature**
```ts
export declare const monoidString: Monoid
```
+**Example**
+
+```ts
+import * as M from 'fp-ts/Monoid'
+
+assert.deepStrictEqual(M.monoidString.concat('a', 'b'), 'ab')
+```
+
Added in v2.0.0
## monoidSum
-Number monoid under addition
+`number` monoid under addition.
+
+The `empty` value is `0`.
**Signature**
@@ -192,6 +344,14 @@ Number monoid under addition
export declare const monoidSum: Monoid
```
+**Example**
+
+```ts
+import * as M from 'fp-ts/Monoid'
+
+assert.deepStrictEqual(M.monoidSum.concat(2, 3), 5)
+```
+
Added in v2.0.0
## monoidVoid
@@ -211,7 +371,7 @@ Added in v2.0.0
**Signature**
```ts
-export interface Monoid extends Semigroup {
+export interface Monoid extends S.Semigroup {
readonly empty: A
}
```
@@ -222,10 +382,23 @@ Added in v2.0.0
## fold
+Given a sequence of `as`, concat them and return the total.
+
+If `as` is empty, return the monoid `empty` value.
+
**Signature**
```ts
export declare function fold(M: Monoid): (as: ReadonlyArray) => A
```
+**Example**
+
+```ts
+import * as M from 'fp-ts/Monoid'
+
+assert.deepStrictEqual(M.fold(M.monoidSum)([1, 2, 3]), 6)
+assert.deepStrictEqual(M.fold(M.monoidSum)([]), 0)
+```
+
Added in v2.0.0
diff --git a/src/Monoid.ts b/src/Monoid.ts
index f0d75315d..adc1e0399 100644
--- a/src/Monoid.ts
+++ b/src/Monoid.ts
@@ -1,84 +1,140 @@
/**
+ * `Monoid` extends the power of `Semigroup` by providing an additional `empty` value.
+ *
+ * ```ts
+ * interface Semigroup {
+ * readonly concat: (x: A, y: A) => A
+ * }
+ *
+ * interface Monoid extends Semigroup {
+ * readonly empty: A
+ * }
+ * ```
+ *
+ * This `empty` value should be an identity for the `concat` operation, which means the following equalities hold for any choice of `x`.
+ *
+ * ```ts
+ * concat(x, empty) = concat(empty, x) = x
+ * ```
+ *
+ * Many types that form a `Semigroup` also form a `Monoid`, such as `number`s (with `0`) and `string`s (with `''`).
+ *
+ * ```ts
+ * import { Monoid } from 'fp-ts/Monoid'
+ *
+ * const monoidString: Monoid = {
+ * concat: (x, y) => x + y,
+ * empty: ''
+ * }
+ * ```
+ *
+ * *Adapted from https://typelevel.org/cats*
+ *
* @since 2.0.0
*/
import { Bounded } from './Bounded'
import { Endomorphism, identity } from './function'
import { ReadonlyRecord } from './ReadonlyRecord'
-import {
- fold as foldSemigroup,
- getDualSemigroup,
- getFunctionSemigroup,
- getJoinSemigroup,
- getMeetSemigroup,
- getStructSemigroup,
- getTupleSemigroup,
- Semigroup,
- semigroupAll,
- semigroupAny,
- semigroupProduct,
- semigroupString,
- semigroupSum,
- semigroupVoid
-} from './Semigroup'
+import * as S from './Semigroup'
/**
* @category type classes
* @since 2.0.0
*/
-export interface Monoid extends Semigroup {
+export interface Monoid extends S.Semigroup {
readonly empty: A
}
/**
- * Boolean monoid under conjunction
+ * `boolean` monoid under conjunction.
+ *
+ * The `empty` value is `true`.
+ *
+ * @example
+ * import * as M from 'fp-ts/Monoid'
+ *
+ * assert.deepStrictEqual(M.monoidAll.concat(true, true), true)
+ * assert.deepStrictEqual(M.monoidAll.concat(true, false), false)
*
* @category instances
* @since 2.0.0
*/
export const monoidAll: Monoid = {
- concat: semigroupAll.concat,
+ concat: S.semigroupAll.concat,
empty: true
}
/**
- * Boolean monoid under disjunction
+ * `boolean` monoid under disjunction.
+ *
+ * The `empty` value is `false`.
+ *
+ * @example
+ * import * as M from 'fp-ts/Monoid'
+ *
+ * assert.deepStrictEqual(M.monoidAny.concat(true, true), true)
+ * assert.deepStrictEqual(M.monoidAny.concat(true, false), true)
+ * assert.deepStrictEqual(M.monoidAny.concat(false, false), false)
*
* @category instances
* @since 2.0.0
*/
export const monoidAny: Monoid = {
- concat: semigroupAny.concat,
+ concat: S.semigroupAny.concat,
empty: false
}
/**
- * Number monoid under addition
+ * `number` monoid under addition.
+ *
+ * The `empty` value is `0`.
+ *
+ * @example
+ * import * as M from 'fp-ts/Monoid'
+ *
+ * assert.deepStrictEqual(M.monoidSum.concat(2, 3), 5)
*
* @category instances
* @since 2.0.0
*/
export const monoidSum: Monoid = {
- concat: semigroupSum.concat,
+ concat: S.semigroupSum.concat,
empty: 0
}
/**
- * Number monoid under multiplication
+ * `number` monoid under multiplication.
+ *
+ * The `empty` value is `1`.
+ *
+ * @example
+ * import * as M from 'fp-ts/Monoid'
+ *
+ * assert.deepStrictEqual(M.monoidProduct.concat(2, 3), 6)
*
* @category instances
* @since 2.0.0
*/
export const monoidProduct: Monoid = {
- concat: semigroupProduct.concat,
+ concat: S.semigroupProduct.concat,
empty: 1
}
/**
+ * `string` monoid under concatenation.
+ *
+ * The `empty` value is `''`.
+ *
+ * @example
+ * import * as M from 'fp-ts/Monoid'
+ *
+ * assert.deepStrictEqual(M.monoidString.concat('a', 'b'), 'ab')
+ *
* @category instances
* @since 2.0.0
*/
export const monoidString: Monoid = {
- concat: semigroupString.concat,
+ concat: S.semigroupString.concat,
empty: ''
}
@@ -87,16 +143,25 @@ export const monoidString: Monoid = {
* @since 2.0.0
*/
export const monoidVoid: Monoid = {
- concat: semigroupVoid.concat,
+ concat: S.semigroupVoid.concat,
empty: undefined
}
/**
+ * Given a sequence of `as`, concat them and return the total.
+ *
+ * If `as` is empty, return the monoid `empty` value.
+ *
+ * @example
+ * import * as M from 'fp-ts/Monoid'
+ *
+ * assert.deepStrictEqual(M.fold(M.monoidSum)([1, 2, 3]), 6)
+ * assert.deepStrictEqual(M.fold(M.monoidSum)([]), 0)
+ *
* @since 2.0.0
*/
export function fold(M: Monoid): (as: ReadonlyArray) => A {
- const foldM = foldSemigroup(M)
- return (as) => foldM(M.empty, as)
+ return S.fold(M)(M.empty)
}
/**
@@ -116,9 +181,9 @@ export function fold(M: Monoid): (as: ReadonlyArray) => A {
*/
export function getTupleMonoid>>(
...monoids: T
-): Monoid<{ [K in keyof T]: T[K] extends Semigroup ? A : never }> {
+): Monoid<{ [K in keyof T]: T[K] extends S.Semigroup ? A : never }> {
return {
- concat: getTupleSemigroup(...monoids).concat,
+ concat: S.getTupleSemigroup(...monoids).concat,
empty: monoids.map((m) => m.empty)
} as any
}
@@ -136,23 +201,45 @@ export function getTupleMonoid>>(
*/
export function getDualMonoid(M: Monoid): Monoid {
return {
- concat: getDualSemigroup(M).concat,
+ concat: S.getDualSemigroup(M).concat,
empty: M.empty
}
}
/**
+ * Unary functions form a monoid as long as you can provide a monoid for the codomain.
+ *
+ * @example
+ * import { Predicate } from 'fp-ts/function'
+ * import * as M from 'fp-ts/Monoid'
+ *
+ * const f: Predicate = (n) => n <= 2
+ * const g: Predicate = (n) => n >= 0
+ *
+ * const M1 = M.getFunctionMonoid(M.monoidAll)()
+ *
+ * assert.deepStrictEqual(M1.concat(f, g)(1), true)
+ * assert.deepStrictEqual(M1.concat(f, g)(3), false)
+ *
+ * const M2 = M.getFunctionMonoid(M.monoidAny)()
+ *
+ * assert.deepStrictEqual(M2.concat(f, g)(1), true)
+ * assert.deepStrictEqual(M2.concat(f, g)(3), true)
+ *
* @category instances
* @since 2.0.0
*/
export function getFunctionMonoid(M: Monoid): () => Monoid<(a: A) => M> {
return () => ({
- concat: getFunctionSemigroup(M)().concat,
+ concat: S.getFunctionSemigroup(M)().concat,
empty: () => M.empty
})
}
+// TODO: swap execution order in v3
/**
+ * Endomorphism form a monoid where the `empty` value is the identity function.
+ *
* @category instances
* @since 2.0.0
*/
@@ -164,6 +251,23 @@ export function getEndomorphismMonoid(): Monoid> {
}
/**
+ * Given a struct of monoids returns a monoid for the struct.
+ *
+ * @example
+ * import * as M from 'fp-ts/Monoid'
+ *
+ * interface Point {
+ * readonly x: number
+ * readonly y: number
+ * }
+ *
+ * const monoidPoint = M.getStructMonoid({
+ * x: M.monoidSum,
+ * y: M.monoidSum
+ * })
+ *
+ * assert.deepStrictEqual(monoidPoint.concat({ x: 1, y: 2 }, { x: 3, y: 4 }), { x: 4, y: 6 })
+ *
* @category instances
* @since 2.0.0
*/
@@ -175,29 +279,53 @@ export function getStructMonoid>(
empty[key] = monoids[key].empty
}
return {
- concat: getStructSemigroup(monoids).concat,
+ concat: S.getStructSemigroup(monoids).concat,
empty
}
}
/**
+ * Get a monoid where `concat` will return the minimum, based on the provided bounded order.
+ *
+ * The `empty` value is the `top` value.
+ *
+ * @example
+ * import * as B from 'fp-ts/Bounded'
+ * import * as M from 'fp-ts/Monoid'
+ *
+ * const M1 = M.getMeetMonoid(B.boundedNumber)
+ *
+ * assert.deepStrictEqual(M1.concat(1, 2), 1)
+ *
* @category instances
* @since 2.0.0
*/
export function getMeetMonoid(B: Bounded): Monoid {
return {
- concat: getMeetSemigroup(B).concat,
+ concat: S.getMeetSemigroup(B).concat,
empty: B.top
}
}
/**
+ * Get a monoid where `concat` will return the maximum, based on the provided bounded order.
+ *
+ * The `empty` value is the `bottom` value.
+ *
+ * @example
+ * import * as B from 'fp-ts/Bounded'
+ * import * as M from 'fp-ts/Monoid'
+ *
+ * const M1 = M.getJoinMonoid(B.boundedNumber)
+ *
+ * assert.deepStrictEqual(M1.concat(1, 2), 2)
+ *
* @category instances
* @since 2.0.0
*/
export function getJoinMonoid(B: Bounded): Monoid {
return {
- concat: getJoinSemigroup(B).concat,
+ concat: S.getJoinSemigroup(B).concat,
empty: B.bottom
}
}