haskell – 宏在多大程度上“反转功能”?

我正在Haskell(code at GitHub)写一个Lisp作为一种学习更多关于这两种语言的方法。

我添加的最新功能是宏。不卫生的宏或什么花哨 – 只是简单的香草码转换。我的初始实现有一个单独的宏环境,与所有其他值居住的环境不同。在读取和eval函数之间,我散布了另一个函数macroExpand,它执行代码树,并在其中找到关键字时执行适当的转换宏环境,最后的表单传递给eval以进行评估。一个很好的优点是宏与其他功能具有相同的内部表示,这减少了一些代码重复。

有两个环境似乎很笨重,而且令我感到烦恼的是,如果我想加载一个文件,那么如果文件包含宏定义,eval就不得不访问宏环境。所以我决定引入宏类型,将宏存储在与函数和变量相同的环境中,并将宏扩展阶段合并到eval中。我一开始就有一些失败的做法,直到我想到我可以写这个代码:

eval env (List (function : args)) = do
    func <- eval env function
    case func of 
        (Macro {}) -> apply func args >>= eval env
        _          -> mapM (eval env) args >>= apply func

它的工作原理如下:

>如果你传递一个包含初始表达式和一堆其他表达式的列表…
>评估第一个表达式
>如果它是一个宏,然后将其应用于参数,并评估结果
>如果它不是一个宏,则评估参数并将该函数应用于结果

就像宏与功能完全一样,除了eval / apply的顺序被切换。

这是宏的准确描述吗?我以这种方式通过执行宏缺少一些重要的东西吗?如果答案是“是”和“否”,那么为什么我从来没有见过这样的宏呢?

答案是“否”和“是”。

看起来你已经开始使用了一个很好的宏模型,其中的宏级别和运行时级别在不同的世界中。事实上,这是Racketmacro system背后的要点之一。您可以阅读关于它的一些简短文本in the Racket guide,或者看到描述此功能的original paper,以及为什么这样做是个好主意。请注意,Racket的宏观系统是一个非常复杂的系统,它是卫生的 – 但相位分离是一个好主意,无论卫生。总结主要优点,它可以始终以可靠的方式扩展代码,因此您可以获得单独编译的优点,而不依赖于代码加载顺序和这些问题。

然后,你进入一个单一的环境,失去了这一点。在大多数Lisp世界中(例如,在CL和Elisp中),这正是事情的完成 – 显然,您遇到上述问题。 (“明显”,因为相分离被设计为避免这些,你刚刚碰巧从与历史上发生的相反的顺序获得你的发现。)无论如何,为了解决其中的一些问题,有eval-when特殊形式,可以指定在运行时或宏扩展时间评估某些代码。在Elisp中,您可以使用eval-when-compile来实现,但在CL中,您可以获得更多的头发,还有其他几个“* -time”。 (CL也有阅读时间,并且拥有一样的环境,一切都是三重的乐趣)。即使它似乎是一个好主意,你应该阅读周围,看看如何一些lispers lose hair because of this mess

在你的描述的最后一步,你进一步回来,发现一些被称为FEXPRs的东西。我甚至不会有任何指针,你可以找到一堆文本,为什么有些人认为这是一个非常糟糕的主意,为什么其他人认为这是一个很好的主意。实际上,这两个“一些”分别是“大多数”和“少数”,尽管少数剩余的FEXPR据点可以是声音。翻译所有这一切:它是爆炸性的东西…提出问题是一个很好的方式来获得长的flamewars。 (作为最近的一个严肃讨论的例子,您可以看到R7RS的初始讨论期,其中FEXPRs出现并导致这些类型的火焰。)无论您选择哪一方,都可以看到:一个是显而易见的:语言与FEXPRs是非常不同于没有它们的语言。 [巧合的是,在Haskell的一个实现工作可能会影响你的观点,因为你有一个地方去一个理智的静态世界的代码,所以“可爱”的超动态语言的诱惑可能更大…]

最后一个注意事项:既然你做类似的事情,你应该研究一个类似的实施一个Scheme in Haskell – IIUC的项目,它甚至有卫生的宏。

http://stackoverflow.com/questions/10249110/to-what-extent-are-macros-functions-in-reverse

本站文章除注明转载外,均为本站原创或编译
转载请明显位置注明出处:haskell – 宏在多大程度上“反转功能”?