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