我正在编写自己的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