2013-05-11 38 views
4

我正在編寫自己的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

+0

你可以實現自動鑽營,或可變參數(處理與*點*在他們arglists)上PARAMS/ARGS長度不匹配(不僅僅是更有趣錯誤)。 :) – 2013-05-11 18:10:08

回答

3

沒有,宏不能用Lisp自身來實現,這是整點給他們。你必須macroexpand每個宏調用根據其定義,作爲加載/編譯/處理給定表達式的一部分。

您將不得不修改您的eval實現,以在未評估的參數上調用宏,並將結果反饋回eval(不是apply,就像處理普通函數應用程序一樣)。如註釋中的sepp2k所示,您可以將宏表示爲Func...表達式,但將它們保存在僅存儲宏的單獨環境中。

還看到:Lazy Evaluation vs Macros

+0

宏如何是一種有效的表達類型?你在想像一個「宏 - 拉姆達」的東西嗎?這不是Lisp處理宏的方式(這是一個宏不是你可以傳遞或存儲在變量中的東西)。或者你說宏*電話*是表達式?這是真的,但在他的'Expr'類型的宏調用中已經被表示爲'List's,其第一個元素是一個'Sym',其中包含一個宏的名字。 – sepp2k 2013-05-11 18:19:41

+0

@ sepp2k不是他的Lisp必須識別'(defmacro ...)'形式,纔有宏?現在它肯定會識別'(lambda ...)並將它們轉換爲'Func ...'表達式。 「Expr」會將「defmacro ...」形式翻譯成哪個?我收集了一些獨特的東西。 – 2013-05-11 18:23:17

+0

@ sepp2k另一種方式當然是將整個宏處理推入實現中。讀取defmacro表單會改變一些內部運行時間,稱'macroexpand-1'會訪問那些內部結構等等。是的,這也是可能的。它仍然適用於「在Lisp本身」實現,甚至更少。 – 2013-05-11 18:30:13

0

你不需要的apply一個特殊版本的LISP實現實現宏系統,只要調用定期apply沒有評估參數,然後evalapply返回的表達。

2

您可以嘗試在計算機程序中的結構與實現閱讀翻譯實現。該eval功能,你清楚地表明僅適用於該默認評價規則,而不是什麼書稱特殊形式

一個正常的Lisp eval功能看起來更像是這樣的:

eval env [email protected](List _) 
    | isSpecialForm env expr = evalSpecial env expr 
    | otherwise = evalApplication env expr 

evalApplication env (op:args) = do 
    func <- eval env op 
    args <- mapM (eval env) args 
    apply func args 

evalSpecial env [email protected](List (op:args)) 
    | isMacro env op = eval env (macroExpand env expr) 
    | otherwise = case op of 
        "lambda" -> ... 
        "if" -> ... 
        -- etc. 
相關問題