這是 cschat.taichung 第 0 次聚會!
由於活動的性質與主題都還沒有確定下來,不對活動主題設限。
- 19:30-20:00 點餐、集合
- 20:00-20:30 導讀 Philip Wadler 的 Comprehending Monads
- 20:30-22:00 自由分享
Comprehending Monads 全文可以在 https://ncatlab.org/nlab/files/WadlerMonads.pdf 找到。
目前挑選的場地沒有投影機,請大家擠在一起盯著同一台電腦吧。
待辦事項:
- 決定下次聚會時間、地點
- 決定一個 logo
- Comprehending Monads by Phlip Wadler
Phlip Wadler 在此文中從 list comprehension 開始,講解它和 monad 的關係,並且以同樣的語法,構造出 identity, strictness, state transformer, state reader, parser, IO 與 continuation monads 。並解釋怎麼將這樣的結構與 call-by-value, call-by-name 的 impure language 連結起來。
介紹了在 pure language 中模擬 impure language 的好用功能(例如改變變數的值)所帶來的麻煩與過往的解決方案,再帶出怎麼靠 monad comprehensions 解決這問題。要注意的是,那時還沒有 do notation 跟 type classes ,很多寫法今天看來很彆扭。
首先介紹 map
的性質,接著將 list comprehension 和 unit
, map
, join
三個 function 對應起來:
-- [t | Λ] = unit t
unit :: a -> m a
-- [t | x <- u] = map (\x -> t) u
map :: (a -> b) -> m a -> m b
-- [t | p, q] = join [[t | q] | p]
join :: m (m a) -> m a
於是:
[(x, y) | x <- [1, 2], y <- [3, 4]]
可以寫成:
[(x, y) | x <- [1, 2], y <- [3, 4]]
join [[(x, y) | y <- [3, 4]] | x <- [1, 2]]
join (map (\x -> [(x, y) | y <- [3, 4]]) [1, 2])
join (map (\x -> (map (\y -> (x, y)) [3, 4])) [1, 2])
這章介紹 identity monad 和 strictness monad ,前者什麼也不做,可以看成 let
,後者控制哪時要 evaluate 成 WHNF(Weak Head Normal Form) 。
identity monad 的 unit
, map
, join
長這樣:
data Id a = a
unitId x = x
mapId f x = f x
joinId x = x
於是:
[(x, y) | x <- 3, y <- 4]
joinId [[(x, y) | y <- 4] | x <- 3]
joinId (mapId (\x -> [(x, y) | y <- 4]) 3)
joinId (mapId (\x -> (mapId (\y -> (x, y)) 4)) 3)
joinId (mapId (\x -> (\y -> (x, y) 4)) 3)
joinId ((\x -> (\y -> (x, y)) 4) 3)
(\x -> (\y -> (x, y)) 4) 3
(3, 4)
strictness 除了 f x
時會把 x
做到 WHNF 外,和 identity 一樣。另外也提到 strictness 會讓 map
的一些性質消失。
介紹了怎麼讓 monad comprehension 幫你把 state 傳下去,而不用每個 function 都手動傳。
data State a = s -> (a, s)
unitState x = \s -> (x, s)
mapState f st = \s -> let (x, s') = st s in (f x, s')
joinState sst = \s -> let (st, s') = sst s; (x, s'') = st s' in (x, s'')
並加上 fetch :: State s
用來讀出現在的 state , assign :: s -> State ()
用來寫入 state , init :: s -> State x -> x
用來提供初始 state 。
fetch :: State s
fetch = \s -> (s, s)
assign :: s -> State ()
assign x = \s -> ((), x)
init :: s -> State x -> x
init s st = let (x, s`) = st s in x
若 state 一開始為 0 ,想加上 1 後存回去,可以這樣做:
init 0 [y | x <- fetch, _ <- assign (x + 1), y <- fetch]
Alex 表示可以看成:
let
s = 0
(x, s') = fetch s
(_, s'') = (assign (x + 1)) s'
(y, s''') = fetch s''
in
y
文中繼續以 lambda calculus 中的名稱替換(alpha equvalence)、更新 array 、 interpreter 為例子。並以 interpreter 告訴我們,有時候要讀複數個值,但是並沒有改變 state ,光靠 state transformer monad 無法描述這件事,引出 state reader monad ,並在兩者間轉換。
介紹如果想在 monad comprehension 中用到 filter ,得多加一個產生空的 monad 的 function zero :: a -> m b
介紹怎麼寫出 monad 間的轉換,並以此證明用 state transformer monad 做的 interpreter 和 加入 state reader monad 的 interpreter 是相等的。
- 介紹怎麼做一個用來處理 lambda calculus 的 parser monad
- 介紹怎麼用 monad 描述 exception ,也就是 maybe monad
- 介紹怎麼樣組織 type ,可以用 monad 描述 IO
- 介紹怎麼用 monad 描述 continuation ,並做出
call/cc
這邊 IO 裡放的是 String
,我猜和當時 IO 仍需要由一個 impure 程式把 String
餵給 pure 程式有關。還不明白後來怎麼演變成描述外在世界變化的 RealWorld
的。
介紹怎樣把 call-by-value, call-by-name language 用 monad comprehension 轉換成 pure lambda calculus ,還強調雖然 call-by-name 在用到時才 evaluate ,但和 call-by-need(lazy) 不同,會有副作用。
然後以 non-deterministic language 採用 call-by-value 與 call-by-name 策略時的不同結果當做例子。
例如 [\a -> a + a | 1 ``choice`` 2]
在 call-by-value 時,結果為 {2, 4}
,在 call-by-name 時,結果為 {2, 3, 4}
。
b4283 認為前者可以理解成 (1 + 1) ``choice`` (2 + 2)
,後者是 (1 ``choice`` 2) + (1 ``choice`` 2)
。
又介紹 call-by-value 如何以 continuation-passing style 來描述作結。
- 原來 Alex 是看到 Meetup 的推薦才來的 XD
- b4283 a.k.a. 德瑞克
- 手寫的 non-deterministic choice operator 被嫌畫得不夠方 XDDD
雖然沒能決定下次聚會主題,但大致決定了方向:
- SKI combinator calculus (b4283)
- 繼續讀 SPJ 的 STG 論文 (caasi)
- SML (b4283)
caasi 希望可以挑 10~15 分鐘可以介紹完的題目,這樣大家都能分享自己想看的主題,也不會給人太大的壓力。
b4283 得開車從中清交流道下來,應該選靠近中清路的場地。
- 時間:2017.10.12(四)
- 地點:可能是多那之咖啡