Re: Build Your Own Haskell Compiler #1.1
坑主在五月底時曾建議可以從最簡單的 context 開始 -- 記錄現在 AST 中含的 expression 數量就好:
type Info = Int然後會有一個叫做 Desugar 的 data :
data Desugar a = D (Info, a)
deriving (Show)根據她的建議:
定義自己的 monad 來攜帶未來要加的資料結構, 讓這個骨幹的運行是在這個 monad 裡
我們必須要實作:
instance Monad Desugar where
return = pure
D (info, module) >>= f = ...但是, return 所代表的 minimum context 是什麼?我該在何時改變 info 來反應 expression 數量更新了呢?光看 return 和 (>>=) 一點頭緒也沒有。事後我才知道, desugar 這件事可以分成兩個問題來看。
其一是如何從 AST 中把我們要的資訊找出來,如果不對 AST 做任何改變,那就是單純把 AST 拆開,經過一些計算再組合回去。這件事可以靠 Applicative 的 (<$>) 和 (<*>) 幫我們做到。
其二是當遇上放在其他 context (例如 Maybe 或 List )中的 Desugar 時,要怎麼把已經找出來的資訊組合起來?這件事情靠 Monad 的 (>>=) 做到。
整個 Haskell.Src.Exts 的 AST 太複雜了,不如先從簡單的 AST 開始。哪裡有簡單的 AST 呢?剛好 BYOHC 前面做過 lambda calculus XD
data Term a = Var a
| Lam a (Term a)
| App (Term a) (Term a)
deriving (Show)現在準備我們的 Desugar :
type Info = Int
data Desugar a = D { runDesugar :: (Info, a) }
deriving (Show)Term 裡面沒有 Maybe 或 List ,於是做到 Applicative 就可以了:
instance Functor Desugar where
fmap f (D (info, term)) = D (info, f term)
instance Applicative Desugar where
pure term = D (0, term)
D (info, f) <*> D (info', t) = D (info + info', f t)為了方便直接寫 desugar 而不是 desugarVar, desugarLam, desugarApp ,來做一個自己的 type class :
-- 需要用到 MultiParamTypeClasses 這個 language extension ,我還不是很明白它的意義與重要性
class (Applicative m) => Desugarable m t where
desugar :: t -> m t然後就可以實作給各種 Term 用的 desugar :
instance Desugarable Desugar (Term a) where
desugar (Var a) = Var <$> pure a
desugar (Lam a term) = Lam <$> pure a <*> desugar term
desugar (App term term') = App <$> desugar term <*> desugar term'看看 Lam a term 和 Lam <$> pure a <*> desugar term ,是不是就像拆開來做些什麼,再組合起來? XD
現在算算有幾個 Lam 。加上陽春的 count function :
increase :: Desugar a -> Desugar a
increase (D (info, term)) = D (info + 1, term)並把 desugar 改成:
instance Desugarable Desugar (Term a) where
desugar (Var a) = Var <$> pure a
desugar (Lam a term) = increase (Lam <$> pure a <*> desugar term)
desugar (App term term') = App <$> desugar term <*> desugar term'之後會再改進這個 increase ,讓它跟 (>>=) 合作,現在先:
main :: IO ()
main =
putStrLn . show . runDesugar . desugar $
App (Lam "x" (Lam "y" (Var "x"))) (Var "z")就能看到它輸出:
(2,App (Lam "x" (Lam "y" (Var "x"))) (Var "z"))的確有 2 個 Lam 。