# Lambda encoding
## 組合子 combinator
- 恆等 `I x = x`
- lambda:`I = \ x . x`
- SKI:`I = S K K`
- Haskell:`id`
- 常數 `K x y = x`
- lambda:`K = \ x y . x`
- Haskell:`const`, `pure` for Applicative `(r →)`
- 融合 `(S f g) x = (f x) (g x)`
- lambda:`S = \ x y z . x z (y z)`
- Haskell:`(<*>)` for Applicative `(r →)`
- [Why is the `S` combinator an S?](https://blog.plover.com/math/combinator-s.html)
- 合成 `(B f g) x = f (g x)`
- lambda:`B = \ x y z . x (y z)`
- SKI:`B = S (K S) K`
- Haskell:`(.)`、`fmap` for Functor `(r →)`
- 交換 `(C f) x y = f y x`
- lambda:`C = \ x y z . x z y`
- SKI:`C = S (S (K K) S) (K K)`
- Haskell:`flip`
- 複製 `(W f) x = f x x`
- lambda:`W = \ x y . x y y`
- SKI:`W = S S (K I)`
- Haskell:`join` for Monad `(r →)`
- `(P f g) x y = f (g x) (g y)`
- lambda:`P = \ x y z w . x (y z) (y w)`
- SKI:`P = ?`
- Haskell:`on`
### 不動點(fixed-point)組合子
如果想定義下面的遞迴函式 `f`(注意到下面的定義裡 `f` 指涉到了自己):
```haskell
f x | terminate x = baseCase x
| otherwise = recurStep f x
```
我們可以定義函式 `F`:
```haskell
F f x | terminate x = baseCase x
| otherwise = recurStep f x
```
然後用不動點組合子 `fix` 實現遞迴:
```haskell
(fix F) x = F (fix F) x
= baseCase x -- if terminate x
= recurStep (fix F) x -- otherwise
```
- `Y = \ f . (\ x . f (x x)) (\ x . f (x x))`
- `X = \ f . (\ x . x x) (\ x . f (x x))`
- `Z = \ f . (\ x . f (\ v . x x v)) (\ x . f (\ v . x x v))`
- `Y' = (\ x y . x y x) (\ y x . y (x y x))`
- `Θ = (\ x y . y (x x y)) (\ x y . y (x x y))`
- Turing fixed-point combinator [Alan Turing]
- `Θv = (\ x y . y (\v. x x y v)) (\ x y . y (\v. x x y v))`
上面的 `Z` 跟 `Θv` 是 `Y` 跟 `Θ` 在及早求值語言裡的版本,就不會無止境:
```
(fix F) x → F (fix F) x → F (F (fix F)) x → ...
```
## 編碼
編碼大致上分為 Church 編碼與 Scott 編碼兩大類,對於非遞迴定義的型態來說這兩類編碼除了參數順序以外都相同。
### Scott 編碼
假設型態 `T` 定義如下
```haskell
data T = C1 X11 X12 … X1m1
| C2 X21 X22 … X2m2
…
| Cn Xn1 Xn2 … Xnmn
```
(或用 GADT 語法定義)
```haskell
{-# LANGUAGE GADTSyntax #-}
data T where
C1 :: X11 -> X12 -> … -> X1m1 -> T
C2 :: X21 -> X22 -> … -> X2m2 -> T
…
Cn :: Xn1 -> Xn2 -> … -> Xnmn -> T
```
建構子 `Cj` 編碼成
```
Cj = \ x1 x2 … xmj . \ c1 c2 … cn . cj x1 x2 … xmj
```
那麼它的資料本身就能反映模式匹配:
```
(Cj x1 x2 … xmj) f1 f2 … fn
= fj x1 x2 … xmj
```
### Church 編碼
與 Scott 編碼不同的是,Church 編碼會自動將應用上的值進一步應用於嵌入建構式的值中。
粗略地說,建構子 `Cj` 可能會編碼成
```
Cj = \ x1 x2 … xmj . \ c1 c2 … cn .
cj (x1 c1 c2 … cn) (x2 c1 c2 … cn) … (xmj c1 c2 … cn)
```
## 布林:`Bool = True | False`
直接 `if cond then else = cond then else`
```
True = \ t f . t
False = \ t f . f
if = I = \ p x y . p x y
```
```
not = \ p . p False True
and = \ p q . p q False
or = \ p q . p True q
```
## 自然數
### Church 編碼
```
Zero = \ f x . x
Succ = \ n . \ f x . f (n f x)
Succⁿ Zero = \ f x . fⁿ x
nest = \ n f x . n f x
```
```
add = \ m n . \ f x . m f (n f x)
mul = \ m n . \ f . m (n f)
pow = \ m n . n m
pred = \ n . \ f x . n (\ g h . h (g f)) (\ u . x) (\ u . u)
```
### 一元 Scott 編碼:`Nat = Zero | Succ Nat`
```
Zero = \ z s . z
Succ = \ n . \ z s . s n
```
### 二元 Scott 編碼:`Nat = Ed | B0 Nat | B1 Nat`
```
Ed = \ e o i . e
B0 = \ n . \ e o i . o n
B1 = \ n . \ e o i . i n
0 = Ed
1 = B1 Ed
2 = B0 (B1 Ed)
3 = B1 (B1 Ed)
4 = B0 (B0 (B1 Ed))
```
```
is-zero = \ n . n True (\ _ . False) (\ _ . False)
double = \ n . n 0 (\ _ . B0 n) (\ _ . B0 n)
succ = \ n . n 1 B1 (\ m . B0 (succ m))
pred = \ n . n 0 (\ m . B1 (pred m)) double
nest = \ f x . \ n . n x (nest f² x) (nest f² (f x))
where f² = \ y . f (f y)
```
如果我們讓 `B0 Ed = Ed`,那麼就要改成下面的樣子
```
is-zero = \ n . n True is-zero (\ m . False)
double = B0
```
## 序對
```
Pair = \ x y . \ f . f x y
fst = \ p . p (\ x y . x)
snd = \ p . p (\ x y . y)
uncurry = \ f p . p f
```
## 列表
### 單序對編碼:`(x, xs)`
```
Nil = False = \ f . I
Cons = Pair
= \ x xs . \ f . f x xs
isNil = \ s . s (\ x xs d . False) True
head = fst
tail = snd
list = \ n f s . s (\ x xs d . f x xs) n
```
機制很酷:
```
list n g Nil
= (\ f . I) (\ x xs d . g x xs) n
= I n
= n
list n g (Cons y ys)
= (\ f . f y ys) (\ x xs d . g x xs) n
= (\ d . g y ys) n
= g y ys
```
### 雙序對編碼:`(isNil, (x, xs))`
```
Nil = Pair True True
Cons = \ x xs . Pair False (Pair x xs)
isNil = fst
head = \ s . fst (snd s)
tail = \ s . snd (snd s)
list = \ n f s . fst s n (snd s f)
```
### Scott 編碼:`List a = Nil | Cons a (List a)`
```
Nil = \ n c . n
Cons = \ x xs . \ n c . c x xs
isNil = \ s . s True (\ x xs . False)
head = \ s . s K (\ x xs . x)
tail = \ s . s Nil (\ x xs . xs)
list = \ n f s . s n f
foldl = \ f e s . s e (\ x xs . foldl f (f e x) xs)
foldr = \ f e s . s e (\ x xs . f x (foldr f e xs))
map = \ f s . s Nil (\ x xs . Cons (f x) (map f xs))
= \ f . foldr (\ x ys . Cons (f x) ys) Nil
```
### Church 編碼:右 fold 編碼
列表代表 `foldr`
```
Nil = \ f e . e
Cons = \ x xs . \ f e . f x (xs f e)
isNil = \ s . s (\ x ys . False) True
head = \ s . s (\ x ys . x) K
tail = \ s . snd (s (\ x p . p (\ xs _ . Pair (Cons x xs) xs)) (Pair Nil Nil))
foldr = \ f e s . s f e
map = \ g s . \ f . s (\ x . f (g x))
= B (C B) (C B)
```
上面用到了 `map`-`foldr` fusion:
```
(map g xs) f e
= foldr f e (map g xs)
= foldr (B f g) e xs
= xs (B f g) e
```
#### biFold
```
biFold = \ f g e s .
s (\ x b k h p . p (b (\ _ . G x) (F x) (K I))
(h x (b (K e) # K))
(\ k h p . k _)
(K e) # K
```
上面的 `#` 可以是任何東西,但若要型態正確的話可改成 `\ _ _ . e`。這能夠實現類似以下的功能:
```haskell
biFold :: (a -> a -> b -> b) -> (a -> b) -> b -> [a] -> b
biFold f g e [] = e
biFold f g e [x] = g x
biFold f g e (x:y:xs) = f x y (biFold f g e xs)
```