受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
答案 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
(隐式用于处理函数应用程序语法)。