Build Your Own Haskell Compiler #1
跟著 BYOHC 做自己的 compiler ,也兩個月了。
目前 C 和 AlexLu 做到 transpiler 。還跟不上。
為了觀察每一步是怎麼 reduce ,近一個月前全部重做,從 LiveScript 移到 ES2015 上。 interpreter 倚賴 Promise 完成 async 操作,於是 internal functions 可以傳回 Promise 好處理 I/O 。
略讀了 The Implementation of Functional Programming Languages , 1987 年流行的作法還是 [Response] -> [Request]
,而不是 I/O monad 。前幾天才找到 SPJ 的 Tackling the awkward squad,想搞清楚 IO 到底在做什麼。
文中提到 IO 很像 state monad (但不是...),還不明白 GHC 是怎麼做的,只好真的用 state monad 做看看:
bind :: IO a -> (a -> IO b) -> IO b
bind = \IOa \f IOa \a \RealWorld pair (f a) RealWorld
於是可以把在 IO a
中的 RealWorld
傳到 IO b
,正如 #haskell.tw 上大家形容的那樣:「把 RealWorld 傳下去」。
問題出在中間的 a -> IO b
裡面用到的 return
,把 b
變成 IO b
的是它,於是它需要知道 IO a
中的 RealWorld
是什麼東西(當然要是我可以把 RealWorld
變不見,又能留下它造成的效果最好,可是我還不懂該怎麼做)。
untyped lambda calculus 中沒有 type class 可用,我又不是用 env 實作 interpreter ,最好只好把 bind
的 type 改成:
bind :: IO a -> ((b -> IO b) -> a -> IO b) -> IO b
那個 b -> IO b
就是 return
。(讓我想起 Angular.js 的 dependency injection )
看 How to desugar Haskell code 說 type class 在 desugar 後會直接呼叫對的 function ,應該不用像我這樣亂改 type 。
接下來沒有 haskell-src-exts 跟 haskell-src 可以用,該怎麼做才好?
這次學到兩件事,一是傻傻找 left-most out-most 的效能有多慘,現在 playground 沒有 delay ,每步都用 requestAnimationFrame ,也要好久才能把讀進去的字串吐出來。難怪那麼多論文都在講,怎麼有效率地 reduce 。二是對 pure functional language 來說,時間順序不重要,只有在和困在時間中的人類互動時,才需要考慮這點。
欸,中二病又發了,快去睡覺比較實在。