我正在編寫自己的LISP,其基礎是在48小時內編寫自己的計劃。 (代碼是here。)作爲最後一個練習,我想實現宏。考慮到我將表達式表示爲不可變數據類型的列表,這怎麼能做到。這可以簡單地在LISP中完成,還是必須在Haskell中實現一些功能?如何在我的LISP中實現一個宏觀系統
我目前的實現是寫在Haskell和幾乎是這樣的:
- 解析輸入並把它變成一個表達式列表
- 評估表情和替換它耕種它是一個單一的表達
- 返回其表達並打印
的表達是在Haskell表示這樣的:
data Expr
= Sym String
| List [Expr]
| Num Int
| Str String
| Bool Bool
| Func Env [String] Expr
| Prim ([Expr] -> ErrorOr Expr)
| Action ([Expr] -> IOErrorOr Expr)
Okey,現在到了真正的問題。宏不會計算其參數,而是通過將參數「放置」到表單中來轉換爲表達式。返回可能作爲引用列表評估或返回的有效表達式。我正在考慮通過一個特殊的評估函數來實現這個功能,它只評估宏表中的符號。如何做到這一點雖然是我有問題的理解。正確的解決方案感覺就像我應該「簡單地」通過用參數替換其中的符號來修改表單,但由於Haskell的不變性,這是不可能的。
因此,Clojure似乎已經在Lisp本身實現宏。我無法解釋Clojure的解決方案,但是如果能做到這一點,感覺就像在Haskell中做到這一點一樣簡單。我不知道macroexpand1(哪個macroexpand調用)會做什麼,它是否在Clojure的實現中調用了一些函數?如果是這樣,那麼我仍然必須在Haskell內部實現它。
如果我們看一下功能如何評估:
eval env (List (op:args)) = do
func <- eval env op
args <- mapM (eval env) args
apply func args
apply :: Expr -> [Expr] -> IOErrorOr Expr
apply (Prim func) args = liftToIO $ func args
apply (Action func) args = func args
apply (Func env params form) args =
case length params == length args of
True -> (liftIO $ bind env $ zip params args)
>>= flip eval form
False -> throwError . NumArgs . toInteger $ length params
apply _ _ = error "apply"
所以,如果我想實現一個宏觀系統,那麼我很可能刪除了參數的評估的一部分,那麼宏參數綁定到它的參數,並且有一個特殊的eval,它只評估表單中的每個符號,然後返回一個新的表單,它將參數放在裏面。這是我不能實現的,我甚至不確定這個邏輯是否正確。
我明白,這個問題很寬,也許可以更簡單地問道,「怎麼我在寫在Haskell
你可以實現自動鑽營,或可變參數(處理與*點*在他們arglists)上PARAMS/ARGS長度不匹配(不僅僅是更有趣錯誤)。 :) – 2013-05-11 18:10:08