程式語言學習心得 7
infix operator 的 precendence 是寫死在文法中的。
才有書中 figure 1.3 這樣結構:
expr -> ...
| ...
| expr1
expr1 -> expr2 | expr1
| expr2
expr2 -> expr3 & expr2
| expr3
expr3 -> ...
直接寫起來會變成:
pExpr1 :: Parser CoreExpr
pExpr1
= ((\e1 op e2 -> EAp (EAp (EVar op) e1) e2) `pFmap` pExpr2 `pAp` (pLit '|') `pAp` pExpr1) `pAlt`
pExpr2
為了避免 expr1
不管成功或失敗,都各 parse expr2
一次,引進了新的資料結構: PartialExpr
。
data PartialExpr = NoOp | FoundOp Name CoreExpr
就可以寫成:
pExpr1 :: CoreExpr
pExpr1 = assembleOp `pFmap` pExpr2 `pAp` pExpr1c
pExpr1c :: PartialExpr
pExpr1c = (FoundOp `pFmap` (pLit "|") `pAp` pExpr1) `pAlt` (pEmpty NoOp)
pExpr2 :: CoreExpr
pExpr2 = assembleOp `pFmap` pExpr3 `pAp` pExpr2c
pExpr2c :: PartialExpr
pExpr2c = (FoundOp `pFmap` (pLit "&") `pAp` pExpr2) `pAlt` (pEmpty NoOp)
-- 略
而把 PartialExpr
組合成 CoreExpr
,則需要:
assembleOp :: CoreExpr -> PartialExpr -> CoreExpr
assembleOp e1 NoOp = e1
assembleOp e1 (FonudOp op e2) = EAp (EAp (EVar op) e1) e2
從現在的角度看,這讓我想起 Maybe
,但是 CoreExpr
和 PartialExpr
就算是 monoid ,也是兩種不同的 monoid ,如果真的要把它們接起來,那 assembleOp
對應的,會是現在的什麼函數呢?我還沒想清楚。