代码改变世界

函数式编程基本概念理解一: Semigroup,Monoid

2023-05-16  本文已影响0人  jimson_ma

实践函数式编程有几个概念是抽象的,但也是基础的,能够正确的理解它们,决定着我们如何更好的使用函数式编程。本文以 FP-TS 为例描述下我对这些概念的理解。

文中大写字符代表一个类型(一组值的集合), 小写字符代表一个值。


Semigroup (半群)


Semigroup 是指一个类型联合一个二元操作*,满足以下的规律:

  1. 对于 A 中的任何 a, b, 通过该操作 *得到的 c 也属于 A, 即 a * b = ca,b,c同属于类型 A.
  2. 该操作满足结合律,即 (a * b) * c = a * (b * c)

我们就称他们为Semigroup

比如我们知道的自然数和加法 +就是满足这个规律的。自然数和乘法 x也是满足的。但是自然数和减法 -就不满足了,两个自然数相减可能是负数,不是自然数,同时减法也不满足结合律。 同样对于字符串和 + 也是满足的。布尔类型和 && 也满足。可以称之为自然数和加法构成的半群,自然数和乘法构成的半群,字符串和 +构成的半群,布尔类型和 && 构成的半群。

在 FP-TS 中定义如下,操作 *concat

interface Semigroup<A> {
  concat: (x: A, y: A) => A
}

concat(concat(x, y), z) == concat(x, concat(y, z)); // true

当我们在逻辑中需要合并等功能时,考虑可否能够使用Semigroup。 它也是我们的功能可否并行执行的基础。

可以自定义Semigroup:

const semigroupSum: Semigroup<number> = {
  concat: (x, y) => x + y
}

Monoid (幺半群)


延续上面的内容,如果存在一个 Semigroup<A> ,可以找到一个唯一的一个值 e,使其能够满足:

  1. concat(x, e) == concat(e, x) == x
  2. e 属于类型 A

e 我们称之为幺元,所以可以理解 Monoid (幺半群) 为存在 幺元Semigroup (半群)

比如正整数和乘法就能构成Monoid (幺半群)1 就是那个幺元,但是正整数和加法就不行,找不到幺元,只能称之为Semigroup (半群),因为它的幺元0, 如果要自然数和加法,则可以是Monoid (幺半群) 。同理字符串和 +里的空字符串''是它的幺元。数组中的空数组等都可以做幺元

在 FP-TS 中定义如下,操作 *concat

interface Monoid<A> extends Semigroup<A> {
  readonly empty: A
}

可以自定义Monoid

/** number `Monoid` under addition */
const monoidSum: Monoid<number> = {
  concat: (x, y) => x + y,
  empty: 0
}

/** number `Monoid` under multiplication */
const monoidProduct: Monoid<number> = {
  concat: (x, y) => x * y,
  empty: 1
}

const monoidString: Monoid<string> = {
  concat: (x, y) => x + y,
  empty: ''
}

/** boolean monoid under conjunction */
const monoidAll: Monoid<boolean> = {
  concat: (x, y) => x && y,
  empty: true
}

/** boolean monoid under disjunction */
const monoidAny: Monoid<boolean> = {
  concat: (x, y) => x || y,
  empty: false
}

参考:
https://dev.to/gcanti/getting-started-with-fp-ts-semigroup-2mf7
https://dev.to/gcanti/getting-started-with-fp-ts-monoid-ja0

上一篇下一篇

猜你喜欢

热点阅读