/
Magma.ts
95 lines (85 loc) · 2.46 KB
/
Magma.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
/**
* A `Magma` is a pair `(A, concat)` in which `A` is a non-empty set and `concat` is a binary operation on `A`
*
* See [Semigroup](https://gcanti.github.io/fp-ts/modules/Semigroup.ts.html) for some instances.
*
* @since 2.0.0
*/
import { Endomorphism } from './Endomorphism'
import { Predicate } from './Predicate'
// -------------------------------------------------------------------------------------
// model
// -------------------------------------------------------------------------------------
/**
* @category model
* @since 2.0.0
*/
export interface Magma<A> {
readonly concat: (x: A, y: A) => A
}
// -------------------------------------------------------------------------------------
// combinators
// -------------------------------------------------------------------------------------
/**
* The dual of a `Magma`, obtained by swapping the arguments of `concat`.
*
* @example
* import { reverse, concatAll } from 'fp-ts/Magma'
* import * as N from 'fp-ts/number'
*
* const subAll = concatAll(reverse(N.MagmaSub))(0)
*
* assert.deepStrictEqual(subAll([1, 2, 3]), 2)
*
* @since 2.11.0
*/
export const reverse = <A>(M: Magma<A>): Magma<A> => ({
concat: (first, second) => M.concat(second, first)
})
/**
* @since 2.11.0
*/
export const filterFirst =
<A>(predicate: Predicate<A>) =>
(M: Magma<A>): Magma<A> => ({
concat: (first, second) => (predicate(first) ? M.concat(first, second) : second)
})
/**
* @since 2.11.0
*/
export const filterSecond =
<A>(predicate: Predicate<A>) =>
(M: Magma<A>): Magma<A> => ({
concat: (first, second) => (predicate(second) ? M.concat(first, second) : first)
})
/**
* @since 2.11.0
*/
export const endo =
<A>(f: Endomorphism<A>) =>
(M: Magma<A>): Magma<A> => ({
concat: (first, second) => M.concat(f(first), f(second))
})
// -------------------------------------------------------------------------------------
// utils
// -------------------------------------------------------------------------------------
/**
* Given a sequence of `as`, concat them and return the total.
*
* If `as` is empty, return the provided `startWith` value.
*
* @example
* import { concatAll } from 'fp-ts/Magma'
* import * as N from 'fp-ts/number'
*
* const subAll = concatAll(N.MagmaSub)(0)
*
* assert.deepStrictEqual(subAll([1, 2, 3]), -6)
*
* @since 2.11.0
*/
export const concatAll =
<A>(M: Magma<A>) =>
(startWith: A) =>
(as: ReadonlyArray<A>): A =>
as.reduce((a, acc) => M.concat(a, acc), startWith)