可扩展的宏定义 [英] Extensible macro definitions

查看:154
本文介绍了可扩展的宏定义的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

评论主题

有没有办法扩展Scheme语法定义,以便它可以使用新定义中的语法的先前定义?此外,这必须是可扩展的,也就是说,必须可以将技术链接在一起几次。

Is there any way to extend a Scheme syntax definition so that it can use the previous definition of the syntax in the new definition? Furthermore, this must be extensible, that is, it must be possible to chain the technique together several times.

例如,假设我们要扩展 lambda 这样每次调用 lambda 定义的函数时,它会在执行函数体之前输出foo。我们可以通过以下方式执行此操作:

For example, say we want to extend lambda so that every time a function defined with lambda is called, it prints "foo" before executing the function body. We can do this in the following way:

(define-syntax old-lambda lambda)

(define-syntax lambda
  (syntax-rules ()
    ((_ args body ...)
     (old-lambda args (display "foo") body ...))))

我们也可以通过另一种方式(例如,通过打印bar)来扩展它以下:

We can also extend this in another way (say, by printing "bar") by doing the following:

(define-syntax old-lambda-2 lambda)

(define-syntax lambda
  (syntax-rules ()
    ((_ args body ...)
     (old-lambda-2 args (display "bar") body ...))))

最终结果是用我们的新 lambda 每次调用时都会打印foo,然后打印bar。

The end result being that functions defined with our new lambda will print "foo", then "bar" each time they are called.

然而,除了使用大量 old-lambda污染命名空间外 - < x> ,这需要在每次执行此操作时在源代码级别创建一个新的 old-lambda-< x> ;这不能自动化,因为你不能在语法定义中使用 gensym 。因此,没有什么好方法可以实现这种可扩展性;唯一合理的解决方案是命名每一个 old-lambda-print-foo 或类似消除歧义的东西,这显然不是一个万无一失的解决方案。 (有关如何失败的示例,请说代码的两个不同部分是扩展 lambda 以打印foo;当然,他们都会将其命名为 old-lambda-print-foo ,瞧! lambda 现在是一个无限循环。)因此,如果它是非常好的我们能够以理想的方式做到这一点:

However, besides polluting the namespace with lots of old-lambda-<x>, this necessitates making a new old-lambda-<x> at the source code level each time we do this; this cannot be automated since you can't, say, use gensym in the syntax definition either. Therefore, there's no good way to make this extensible; the only plausible solution is naming each one old-lambda-print-foo or something similar to disambiguate, which is obviously not a foolproof solution. (For an example of how this could fail, say two different parts of the code were to extend lambda to print "foo"; naturally, they would both name it old-lambda-print-foo, and voila! lambda is now an infinite loop.) Therefore, it would be very nice if we were able to do this in a way which ideally:


  • 不要求我们用大量的<$ c $污染命名空间c> old-lambda-< x>

  • 或者,如果失败,保证我们不会发生碰撞。

推荐答案

在Racket中,您可以使用模块执行此操作。您可以创建一个模块,重新导出整个Racket语言,但Racket的 lambda 除外,并以名称 lambda 。我将展示一种安排​​代码的方法。

In Racket, you can do this with modules. You could create a module that re-exports the entire Racket language except for Racket's lambda, and exports your new macro under the name lambda. I'll show one way to arrange the code.

foo-lambda 模块定义并导出 foo-lambda 表单,它创建在应用时打印foo \ n的过程。

The foo-lambda module defines and exports the foo-lambda form, which creates procedures that print "foo\n" when applied.

(module foo-lambda racket
  (define-syntax-rule (foo-lambda formals body ...)
    (lambda formals (displayln "foo") body ...))
  (provide foo-lambda))

球拍 - 带 - foo-lambda 模块重新导出整个Racket语言,除了它在名称 lambda foo-lambda c $ c>。

The racket-with-foo-lambda module re-exports the entire Racket language except it provides foo-lambda under the name lambda.

(module racket-with-foo-lambda racket
  (require 'foo-lambda)
  (provide (except-out (all-from-out racket) lambda)
           (rename-out [foo-lambda lambda])))

现在你可以用这种新语言编写一个模块:

Now you can write a module in this "new language":

(module some-program 'racket-with-foo-lambda
  (define f (lambda (x) x))
  (f 2))
(require 'some-program)

请注意这不会更改 lambda 的Racket版本,而其他Racket表单仍然使用Racket lambda 绑定。例如,如果您将 f 的定义重写为(define(fx)x),那么Racket的 define 会扩展为使用Racket的 lambda ,你会获得foo打印输出。

Note that this doesn't change the Racket version of lambda, and other Racket forms still use the Racket lambda binding. For example, if you rewrote the definition of f above as (define (f x) x), then Racket's define would expand into a use of Racket's lambda, and you would not get the "foo" printout.

您可以链接扩展:每个扩展都在导入先前版本的模块中定义。例如,您的 bar-lambda 模块将导入 foo-lambda 模块,依此类推。

You can chain extensions: each extension is defined in a module that imports the previous version. For example, your bar-lambda module would import the foo-lambda module, and so on.

实际上,Racket在内部执行此操作。编译器只能使用位置参数理解 lambda ,但是Racket语言有 lambda ,它支持位置和关键字参数。 Racket语言的实现有一个模块替换内置的 lambda #%app (隐含用于处理函数应用程序语法),其中包含处理关键字参数的版本。

Racket does this internally, in fact. The compiler only understands lambda with positional arguments, but the Racket language has a lambda that supports both positional and keyword arguments. The implementation of the Racket language has a module that replaces the built-in lambda and #%app (implicitly used to handle function application syntax) with versions that handle keyword arguments.

这篇关于可扩展的宏定义的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆