Inheritance 可扩展宏定义

Inheritance 可扩展宏定义,inheritance,macros,scheme,r5rs,Inheritance,Macros,Scheme,R5rs,灵感来源于一个关于函数而非宏的相关问题 有没有办法扩展Scheme语法定义,以便它可以在新定义中使用以前的语法定义?此外,这必须是可扩展的,也就是说,必须能够多次将技术链接在一起 例如,假设我们希望扩展lambda,以便每次调用使用lambda定义的函数时,它都会在执行函数体之前打印“foo”。我们可以通过以下方式实现这一点: (define-syntax old-lambda lambda) (define-syntax lambda (syntax-rules () ((_ a

灵感来源于一个关于函数而非宏的相关问题

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

例如,假设我们希望扩展
lambda
,以便每次调用使用
lambda
定义的函数时,它都会在执行函数体之前打印“foo”。我们可以通过以下方式实现这一点:

(define-syntax old-lambda lambda)

(define-syntax lambda
  (syntax-rules ()
    ((_ args body ...)
     (old-lambda args (display "foo") body ...))))
我们还可以通过执行以下操作以另一种方式(例如,通过打印“条”)扩展此功能:

(define-syntax old-lambda-2 lambda)

(define-syntax lambda
  (syntax-rules ()
    ((_ args body ...)
     (old-lambda-2 args (display "bar") body ...))))
最终结果是,使用新的
lambda
定义的函数将在每次调用时打印“foo”,然后打印“bar”

然而,除了用大量的
旧lambda-
污染名称空间之外,每次我们这样做时,都需要在源代码级别创建一个新的
旧lambda-
;这不能自动化,因为您也不能在语法定义中使用
gensym
。因此,没有好的方法使它可扩展;唯一可行的解决方案是将每一个
命名为old lambda print foo
或类似于消除歧义的东西,这显然不是一个万无一失的解决方案。(举一个如何失败的例子,假设代码的两个不同部分扩展了
lambda
来打印“foo”;很自然,他们都会将其命名为
old lambda print foo
,瞧!
lambda
现在是一个无限循环。)因此,如果我们能够以一种理想的方式来做这件事,那将是非常好的:

  • 不需要我们用大量的
    旧lambda-
  • 否则,我们就不能保证不会发生碰撞

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

foo lambda
模块定义并导出
foo lambda
表单,该表单创建应用时打印“foo\n”的过程

(module foo-lambda racket
  (define-syntax-rule (foo-lambda formals body ...)
    (lambda formals (displayln "foo") body ...))
  (provide foo-lambda))
racket with foo lambda
模块重新导出整个racket语言,除了在名称
lambda
下提供
foo lambda

(module racket-with-foo-lambda racket
  (require 'foo-lambda)
  (provide (except-out (all-from-out racket) lambda)
           (rename-out [foo-lambda lambda])))
现在您可以用这种“新语言”编写一个模块:

请注意,这不会更改
lambda
的球拍版本,其他球拍形式仍然使用球拍
lambda
绑定。例如,如果您将上述
f
的定义重写为
(define(f x)x)
,则Racket的
define
将扩展为使用Racket的
lambda
,并且您将不会得到“foo”打印输出

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


事实上,Racket是在内部实现这一点的。编译器只理解带有位置参数的
lambda
,但Racket语言具有同时支持位置参数和关键字参数的
lambda
。Racket语言的实现有一个模块,用处理关键字参数的版本替换内置的
lambda
#%app
(隐式用于处理函数应用程序语法)。

我注意到的一个限制是为了链接这些扩展,每个模块都需要导入上一个模块。有没有可能做到这一点,让不同的扩展彼此独立,这样我就可以,比如说,选择我想要使用的扩展,按我的意愿排序,等等?这更复杂,但是是的。您可以使用宏对定义进行抽象,例如
define foo lambda
,该宏为要使用的类
lambda
基表单获取标识符。这似乎与问题中的方法遇到了相同的困难。
(module some-program 'racket-with-foo-lambda
  (define f (lambda (x) x))
  (f 2))
(require 'some-program)