可扩展的宏定义

时间:2014-05-27 21:04:09

标签: inheritance macros scheme r5rs

comment thread启发,涉及有关功能而非宏的相关问题。

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

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

(define-syntax old-lambda lambda)

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

我们还可以通过以下方式扩展这一点(例如,通过打印“bar”):

(define-syntax old-lambda-2 lambda)

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

最终结果是用我们的新lambda定义的函数将打印“foo”,然后在每次调用它们时“bar”。

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

  • 不要求我们使用大量lambda
  • 污染名称空间
  • 或者,如果不这样做,保证我们不会发生碰撞。

1 个答案:

答案 0 :(得分:1)

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

foo-lambda模块定义并导出foo-lambda表单,该表单创建打印&#34; foo \ n&#34;适用时。

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

racket-with-foo-lambda模块重新导出整个Racket语言,但它以名称foo-lambda提供lambda

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

现在你可以用这个&#34;新语言编写一个模块&#34;:

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

请注意,这不会更改<{em>} lambda的Racket版本,而其他Racket表单仍然使用Racket lambda绑定。例如,如果您将上面f的定义重写为(define (f x) x),那么Racket的define将扩展为使用Racket的lambda,并且你得到&#34; foo&#34;打印输出。

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

实际上,球拍在内部完成了这项工作。编译器只能使用位置参数来理解lambda,但是Racket语言有lambda,它支持位置和关键字参数。 Racket语言的实现有一个模块,用于处理关键字参数的版本替换内置的lambda#%app(隐式用于处理函数应用程序语法)。