Re: Build Your Own Haskell Compiler #1.2
明明只是入門,卻讓苦惱了半年,程度真差。
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
成為了 Functor 還有 Applicative ,於是可以讓一個函式作用在被 Desugar
包起來的 Term
上面(fmap :: (a -> b) -> f a -> f b
)。也有了最小最小的 context (pure :: a -> f a
),跟將收在 Desugar
中的函式作用在被包起來的其他東西的辦法((<*>) :: f (a -> b) -> f a -> f b
)。
再來我好奇的是,他們符合各自該符合的 laws 嗎?LYaH 教我 Functor 和 Applicative 得符合某些規定,而那些規定是 compiler 沒辦法幫忙驗證的。
Functor Laws
fmap id = id
famp (p . q) = fmap p . fmap q
Functor Laws 有兩個,一是 fmap id
和 id
作用起來是一樣的。 Desugar
的 fmap
對裡面的 Term
和藏起來的 Info
沒有做多餘的事情,很明顯滿足這點。另外一條是 fmap (p . q)
和 fmap p . fmap q
作用起來是一樣的,如果從「打開...做事情...關起來」的角度看,那前者是打開來做兩件事然後關起來,後者是打開做一件事,關起來,然後再打開做一件事情,再關起來。也很明顯滿足了。
注意到 fmap
的數量,在第一條 law 裡,從 1 個變成 0 個,在第二條 law 裡,從 1 個變成 2 個,我還不知道該怎麼理解這件事、或是這件事有沒有什麼意義。
Applicative Functor Laws
pure id <*> v = v
pure f <*> pure x = pure (f x)
u <*> pure y = pure ($ y) <*> u
pure (.) <*> u <*> v <*> w = u <*> (v <*> w)
第一條 pure id <*> v = v
可以說是 fmap id = id
的進階版(或者寫成 fmap id a = a
比較一致?),只是前者的 id
先放在一個最小的 context 中了。
第二條 pure f <*> pure x = pure (f x)
,也算明顯。對 Desugar
來說,最小 context 裡的 Info
都是 0
,相加和本來一樣沒有變化,也符合這條 law 。
第三條 u <*> pure y = pure ($ y) <*> u
,我想重點也不是在 u
裡面的函式怎麼作用在 y
上,而是在 pure
從右邊換到左邊的過程中,是不是不造成多餘的改變?在 Desugar
中, pure
在右邊會變成 info + 0
,和 pure
在左邊的 0 + info
是一樣的。
第四條我一開始是看不明白的,現在看來,對 Desugar
來說,是表示 0 + u的info + v的info + w的info
與 u的info + (v的info + w的info)
不該有差別。
再來想加上被 []
包起來的 Term
,然後需要讓 Desugar
也是個 Monad
,接著配上個和 do notation 合作起來更融洽的 increase
,就可以把這一切用在 haskell-src-exts 給我們的 AST 上了。