【问题标题】:Are there whole-program-transforming macros in Lisp or Scheme?Lisp 或 Scheme 中是否有整个程序转换的宏?
【发布时间】:2011-06-13 02:44:44
【问题描述】:

我看过一个How does Lisp let you redefine the language itself?的回答 Stack Overflow 问题(由 Noah Lavine 回答):

宏并不是对语言的完全重新定义,至少据我所知(我实际上是一个计划者;我可能是错的),因为有一个限制。宏只能获取代码的单个子树,并生成单个子树来替换它。因此,你不能编写整个程序转换的宏,那样会很酷。

阅读本文后,我很好奇 Lisp 或 Scheme(或其他语言)中是否存在“整个程序转换宏”。

如果不是那为什么?

  • 它没用,也从来不需要?
  • 同样的事情可以通过其他方式实现吗?
  • 即使在 Lisp 中也无法实现?
  • 有可能,但从未尝试或实施过?

更新

一种用例 例如

在 stumpwm 代码中 这里有一些函数都在不同的 lisp 源文件中 使用在 primitives.lisp 中定义但在 screen.lisp 中使用的动态/全局 defvar 变量 *screen-list* >user.lispwindow.lisp。 (这里每个文件都有与一个方面或对象相关的函数、类、变量)

现在我想在闭包下定义这些函数 *screen-list* 变量可以通过 let 形式获得,它不应该是 动态/全局变量,但没有将这些所有函数移入 一个地方(因为我不希望这些功能从它们的 相关文件) 这样这个变量就只能被这些函数访问。

上面例如同样适用于 label 和 flet,以便进一步可能 我们可以让它像只需要的变量,函数将可用, 给需要的人。

注意一种方法可能是 为 defun 实现并使用一些宏 defun_with_context ,其中第一个参数是 上下文 let, flet 变量定义。 但除此之外,它可以通过 reader-macro 实现 Vatine 和 Gareth Rees 回答了。

【问题讨论】:

    标签: macros lisp scheme transformation


    【解决方案1】:

    你引用诺亚·拉文的话:

    宏只能获取代码的单个子树,并生成单个子树来替换它

    这是普通宏的情况,但是阅读器宏可以访问输入流,并且可以用它做任何他们喜欢的事情。

    请参阅 Hyperspec section 2.2set-macro-character 函数。

    【讨论】:

      【解决方案2】:

      总是可以选择使用编译器宏(它们可以根据一些标准进行全功能转换,但不应更改返回的值,因为这会造成混淆)。

      有阅读器宏,它们会在“读取时”(或“在读取之前”,如果您愿意的话)转换输入。我没有做过太多大规模的阅读器宏黑客攻击,但我已经编写了一些代码来允许(大部分)在 Common Lisp 中读取 elisp sourec,两者之间的语法糖有相当多的细微差别。

      【讨论】:

        【解决方案3】:

        一种典型的方法是编写自己的模块系统。如果您只想访问所有代码,则可以使用某种预处理器或阅读器扩展包装源文件,并使用您自己的模块注释。如果您随后编写自己的requireimport 表单,您最终将能够看到范围内的所有代码。

        首先,您可以编写自己的module 表单,让您定义几个函数,然后在发出优化代码之前以某种巧妙的方式编译这些函数。

        【讨论】:

          【解决方案4】:

          在我的脑海中,一些方法:

          首先,你可以。 Norvig points out那个:

          我们可以将编译器编写为一组宏。

          因此,如果您愿意,您可以转换整个程序。我只是很少看到它完成,因为通常“你想对程序的每个部分做的事情”和“你需要宏/AST类型转换的事情”之间的交集是一个很小的集合。一个例子是Parenscript,它将您的 Lisp 代码(“CL 的扩展子集”)转换为 Javascript。我用它把整个 Lisp 代码文件编译成 Javascript,直接提供给 Web 客户端。这不是我最喜欢的环境,但它做它所宣传的。

          另一个相关的功能是“建议”,Yegge describes as:

          伟大的系统也有建议。此功能没有普遍接受的名称。有时它被称为钩子、过滤器或面向方面的编程。据我所知,Lisp 最先拥有它,在 Lisp 中它被称为建议。 Advice 是一个迷你框架,提供 before、around 和 after 挂钩,您可以通过这些挂钩以编程方式修改系统中某些操作或函数调用的行为。

          另一个是special variables。通常宏(和其他结构)适用于词法范围。通过声明一个变量是特殊的,你告诉它适用于动态范围(我认为它是“时间范围”)。我想不出任何其他语言可以让您(程序员)在这两者之间进行选择。而且,除了编译器案例之外,这两个确实跨越了我作为程序员感兴趣的领域。

          【讨论】:

            【解决方案5】:

            我相信这类宏称为代码遍历宏。我自己没有实现代码步行器,所以我不熟悉限制。

            【讨论】:

              【解决方案6】:

              至少在 Common LISP 中,您可以将顶级表单包装在 PROGN 中,并且它们仍然保留其作为顶级表单的状态(请参阅CLTL2, section 5.3)。因此,生成单个子树的宏的限制并不是很大的限制,因为它可以在 PROGN 中包装任意数量的结果子树。这使得整个程序的宏成为可能。

              例如

              (my-whole-program-macro ...)
              
              = expands to =>
              
              (progn
                (load-system ...)
                (defvar ...)
                (defconstant ...)
                (defmacro ...)
                (defclass ...)
                (defstruct ...)
                (defun ...)
                (defun ...)
                ...
                )
              

              【讨论】:

                【解决方案7】:

                在 Racket 中,您可以实现整个程序转换宏。请参阅文档中有关 defining new languages 的部分。在 Racket 中有很多这样的例子,例如惰性语言和 Typed Racket。

                【讨论】:

                  猜你喜欢
                  • 2018-11-15
                  • 2011-10-17
                  • 2010-10-31
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-02-22
                  • 1970-01-01
                  • 2011-04-20
                  相关资源
                  最近更新 更多