# Functor 與 JS/TS
###### tags: `Functional Programming` `note`
用數學角度出發去看 **Functor** 的定義什麼,我們怎麼用 `Typescript` 實現它,以及 `Option`、`List` 幫我們做了什麼。

## 定義
根據[維基百科](https://en.wikipedia.org/wiki/Functor)描述「Functor 是範疇之間的映射」,而且要視為 Functor 要達成幾項條件:
先假設有 $C$ 與 $D$ 兩個範疇,而 functor $F$ 是 $C$ 與 $D$ 之間的映射
- $C$ 範疇中的每個 object $X$ 都與 $D$ 之中每個 $F(X)$ 相關聯
- 將 $C$ 中的每個 morphism $f: X \to Y$ 關聯到 $D$ 中的 morphism $F(f): F(X) \to F(Y)$ 並且達成以下兩個條件:
- 對於 $C$ 中的每個 $X$,$F(id_x)=id_{F(x)}$
- 對於 $C$ 中的 $f:X \to Y$ 和 $g:Y \to Z$ 等所有 morphism ,$F(g\circ f)=F(g)\circ F(f)$

其他關於 Functor 定義的參考連結
- [Functor and Natural Transformation](https://categorytheory.gitlab.io/functor_and_natural_transformation.html)
- [What is a Functor? Definition and Examples, Part 1](https://www.math3ma.com/blog/what-is-a-functor-part-1)
每篇文章對 Functor 定義可能有些許不同,但你能找出他們之間的共同點,而那個共通點就是 Functor 的公認定義。
### 建立一個 Functor
我們需要一個從範疇 $C$ 轉到範疇 $D$ 的 function,而範疇 $C$ 在程式中通常代表了所有原始型別與自定義型別之間的關係,範疇 $D$ 表示另一個空間與範疇 $C$ 互相對應。
先定義一個 Functor
```typescript
interface Functor<T>{
value:T
}
// X -> F(X)
function of<T>(value:T): Functor<T> {
return {
value: value
}
}
```
`of` 就是將範疇 $C$ 的 object 帶到範疇 $D$ 的一個函式。
### map / fmap
但是當我們今天在範疇 $C$ 有個 morphism 是 $f:X\to Y$,那範疇 $D$ 也要有相應的 morphism,但總不可能 $C$ 有多少個 morphism,$D$ 也跟著建多少個 morphism,所以我們 $D$ 需要一個 `map` 函式將 $C$ 的 morphism 映射過去。
```typescript
interface Functor<T>{
value:T
map<U>:(fn: (v:T) => U): Functor<U>;
}
// X -> F(X)
function of<T>(value:T): Functor<T> {
return {
value: value
map: (fn)=> of(fn(value))
}
}
```
我們來實際運行看看是否有達成 Functor 定義
$X\to F(X)$
```typescript
// X
const x: number = 10;
// F(X)
const fx: Functor<number> = of(x);
```
$f:X\to Y$ -> $F(f:X\to Y)$
```typescript
const f = (x: number): string => x.toString();
const fx: Functor<number> = of(10);
const fy: Functor<string> = fx.map(f);
```
$F(g\circ f)=F(g)\circ F(f)$
```typescript
const f = (x) => x * x; // number -> number
const g = (y) => y.toString(); // number -> string
const fx: Functor<number> = of(10);
const fz1: Functor<string> = fx.map((x) => g(f(x)));
const fz2: Functor<string> = fx.map(f).map(g);
```
`fz1` 跟 `fz2` 結果是相同的
### 建完了 Functor 然後呢?
我們做了一個 Functor,但是看不出來有什麼用。確實沒有什麼用,就只是把值帶入 $D$ 範疇裡而已,不過我們可以對 $D$ 動手腳,並且只要我們有達成條件,那就還是 Functor。
---
## 創造一個「有」跟「沒有」的世界
既然我們要看出 Functor 的實際效果,那我們需要加點魔法,我們在寫程式時常常需要判斷欄位或者值存不存在,確定存在才往下執行。
```typescript
function fn(value: number | undefined): numbner | undefined {
if(value !== undefined){
return value * 2 + 50;
}
return undefined;
}
```
看起來還好,不過當量多起來的時候你會變得很煩躁,每次都要檢查值存不存在,難道我不能只說我要怎麼算,是不是空的無所謂,空的就自動幫我跳過。
### Option / Maybe
**Option** 跟 **Maybe** 就是在解決這個問題,讓我們來實作一個 `Option`
```typescript
interface IOption<T> {
map: <U>(fn: (value: T) => U) => Option<U>;
}
class Some<T> implements IOption<T> {
value: T;
constructor(value: T) {
this.value = value;
}
map<U>(fn: (value: T) => U): Some<U> {
return new Some(fn(this.value))
}
}
class None implements IOption<undefined> {
value: undefined;
constructor(value?: any){
this.value = undefined;
}
map<U>(fn: (value: any) => U): None {
return this;
}
}
type Option<T> = Some<T> | None;
function of<T>(value: T): Option<T>{
if(value === undefined) return new None();
return new Some(value);
}
```
上方只是個範例,實作方法以及 `null` 該不該認定為空值都取決於開發者本身。
那我們把原本的範例修改一下:
```typescript
function fn(value: Option<number>): Option<number>{
return value.map((v) => v * 2 + 50);
}
```
而在傳進去之前將一個不確定是不是 `number` 的值用 `of` 轉成 `Option<number>`,一切一如往常,運算式照算並且不需要一直判斷,只有真正要用到這個值時你才要確認他到底存不存在。
所以我們在 `of` 跟 `map` 上做了一些調整,如果以單純 Type 來看,還是達成了 Functor 的條件
- $X\to F(X)$
```typescript
of = T -> Option<T>
```
- $f:X\to Y$ -> $F(f:X\to Y)$
```typescript
const f = (x: number): string => x.toString();
const optionX: Option<number> = of(10);
const optionY: Option<string> = optionX.map(f);
```
- $F(g\circ f)=F(g)\circ F(f)$
```typescript
const f = (x) => x * x; // number -> number
const g = (y) => y.toString(); // number -> string
const optionX: Option<number> = of(10);
const optionZ1: Option<string> = optionX.map((x) => g(f(x)));
const optionZ2: Option<string> = optionX.map(f).map(g);
```
`optionZ1` 與 `optionZ2` 結果會相同
非常神奇對吧,我們把判斷 **有** 跟 **沒有** 這件事隱藏起來了,而另一個常見的 Functor 都藏匿在我們日常開發中,那就是 **Array** / **List**。
---
## Array / List,把 for loop 隱藏起來
在日常開發中我們常需要對整個 Array 每個值作一些運算,所以最原始的方法都是用 for loop。
```typescript
function fn(arr: Array<number>): Array<string>{
const newArr = [];
for(num of arr){
newArr.push(num.toString());
}
return newArr;
}
```
好像也還好,但是當今天你有更多樣化的需求時怎麼辦?可能是時間轉換、取物件裡某個欄位等等,那你可能每次都要重新寫一個 for loop function 處理這件事,非常的麻煩且無意義。
在 JS 中其實已經有 `map` method 讓你用了,那我們來看是否都達成了 Functor 的條件
- $X\to F(X)$
```typescript
const arr: Array<number> = Array.of(1); // 或任何其他創建 Array 的方式
```
- $f:X\to Y$ -> $F(f:X\to Y)$
```typescript
const f = (x: number): string => x.toString();
const arrX: Array<number> = Array.of(1, 2, 3);
const arrY: Array<string> = arrX.map(f);
```
- $F(g\circ f)=F(g)\circ F(f)$
```typescript
const f = (x) => x * x; // number -> number
const g = (y) => y.toString(); // number -> string
const arrX: Array<number> = of(1, 2, 3);
const arrZ1: Array<string> = arrX.map((x) => g(f(x)));
const arrZ2: Array<string> = arrX.map(f).map(g);
```
`arrZ1` 與 `arrZ2` 結果會相同
我們把枯燥乏味的 for loop 隱藏起來了!
## 結語
我們從數學上的定義與在 FP 語言中常出現的 Functor type 做了對應,以了解數學與程式之間如何配合,以及這些 Functor 解決了我們日常開發的哪些問題,甚至 `Either` 也可以自己試著了解並實作。