2013-05-11 68 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.