# Semigroup Algebraic structure ###### tags: `Typescript` `Functional Programming` `fp-ts` `algebra` **Semigroup**,為代數結構的一種,也是 **Magma** 的延伸,從 **Magma** 添加一條[**結合律(Associative Property)**](https://zh.wikipedia.org/zh-tw/%E7%BB%93%E5%90%88%E5%BE%8B) 的規則。 ### 結合律 首先我們定義一個 Magma 為 $(M,\ \bullet)$,從 $M$ 隨機取出三個元素(可以超過三個) $x,\ y,\ z$,將其作運算,而運算的優先度有以下幾種變化: 1. $((x\bullet y)\bullet z)$ 2. $(x\bullet (y\bullet z))$ 3. $x\bullet y\bullet z$ $x\bullet y\bullet z$ 這個 case 看起來是合理的,但在 Magma 中的定義的是[**二元運算(Binary operation)**](https://en.wikipedia.org/wiki/Binary_operation),所以不會討論這個 case。 而結合律必須滿足 $$((x\bullet y)\bullet z) = (x\bullet (y\bullet z))$$ 也就是說**不管元素有多少,執行計算優先度如何換,最終都不影響結果**,最常見的就是加法跟乘法: $$((1+2)+3) = (1+(2+3)) = 6$$ $$((1*2)*3) = (1*(2*3)) = 6$$ 在 [***fp-ts*** interface](https://github.com/gcanti/fp-ts/blob/master/src/Semigroup.ts#L57) 為 ```typescript interface Semigroup<A> extends Magma<A> {} ``` 竟然就是 **Magma**!為什麼呢? 因為以型別來說 TS 無法知道你的運算(concat)到底有沒有遵守**結合律**,這是我們自己要去證明或確定的,以這裡來說只是給我們使用時識別這個 type 運算時需不需要遵守結合律。 我們用先前 **Coord Magma** 的例子來看有沒有遵守結合律: ```typescript! type Coord = { x: number; y: number; }; const MagmaCoordAdd: Magma<Coord> = { concat: (first, second) => ({ x: first.x + second.x, y: first.y + second.y }), }; const coordA: Coord = { x: 2, y: 5 }; const coordB: Coord = { x: 4, y: 8 }; const coordC: Coord = { x: 1, y: 7 }; // ((a + b) + c) const res1 = MagmaCoordAdd.concat(MagmaCoordAdd.concat(coordA, coordB), coordC); // (a + (b + c)) const res2 = MagmaCoordAdd.concat(coordA, MagmaCoordAdd.concat(coordB, coordC)); res1 === res2 // true ``` 自己嘗試過後是有的,所以我們可以說 type **Coord** 相加是一個 **Semigroup**。 ### 其他案例 還有些在程式裡擁有 **Semigroup** 特性的 **Magma**,像是**字串相加、&&、||**。 ```typescript ('a' + 'b') + 'c' === 'a' + ('b' + 'c') (true && false) && true === true && (false && true) (true || false) || false === true || (false || false) ``` 而在這篇[文章](https://github.com/enricopolanski/functional-programming#definition-of-a-semigroup)中提了兩個有趣的案例,可以自己實驗看看 ```typescript import { Semigroup } from 'fp-ts/Semigroup' /** Always return the first argument */ const first = <A>(): Semigroup<A> => ({ concat: (first, _second) => first }) ``` ```typescript import { Semigroup } from 'fp-ts/Semigroup' /** Always return the second argument */ const last = <A>(): Semigroup<A> => ({ concat: (_first, second) => second }) ``` ### 參考資訊 - https://github.com/enricopolanski/functional-programming#definition-of-a-semigroup - https://en.wikipedia.org/wiki/Semigroup - https://en.wikipedia.org/wiki/Binary_operation - https://en.wikipedia.org/wiki/Associative_property