是否可以剥离对词法变量进行内部定义的访问?
也许这不是问题,但有时我希望我可以定义内部程序而无需访问封闭范围。考虑这个例子:
(define (usual-racket n)
(define (hi a)
(displayln n))
(hi 'hi)
n)
我在这里"意外地"在内部定义的过程n
中键入a
而不是hi
。因为n
在定义时是可见的,所以球拍不会抱怨。是否有某种方法可以创建一个像define-free
这样的特殊形式来放弃这个封闭的上下文?我认为使用strip-context
会起作用,但显然我没有正确思考,因为这不起作用:
(define-syntax (define-free stx)
(let ((s (strip-context stx)))
(syntax-case s ()
((_ (name args ...) body ...)
#'(define (name args ...) body ...)))))
如果我有一种新的外部 define
,那么它确实有效:
(define-syntax (define/free stx)
(syntax-case stx (define-free)
((_ (N A ...) (define-free (n a ...) b ...) B ...)
#'(define N
(let ((n (lambda (a ...) b ...)))
(lambda (A ...) B ...))))))
(define/free (definitely-works n)
(define-free (hi a)
(displayln n)) ; n: unbound identifier in module in: n
(hi 'hi)
n)
...但我希望有一个解决方案不需要重新定义基础define
语法并尝试管理内部定义可能显示其头部的所有位置。
澄清编辑 我的动机不是要解决上面的问题,这只是一个范围示例。我的动机主要是以这样的方式管理范围,以帮助我的代码的读者(主要是:我)理解这个过程在其封闭范围内的位置。我使用的许多内部定义都是帮助程序,使代码体更容易编写和理解。这样的帮助者不需要访问任何范围 - 他们也可能是顶级定义。但是,将程序置于最高级别,无论是好还是坏,往往表明它存在,因为要了解各种程序,你必须牢记这个程序的定义。所以"表面"如果这些内部定义在编写时被提升到顶层,那么模块会变得更大。
考虑一下:
(define (fib n)
(define (f index n-2 n-1)
(if (< index 1)
n-1
(f (sub1 index) n-1 (+ n-2 n-1))))
(f n 0 1))
内部程序f
仅用于管理计算fib
所需的其他状态。它不需要fib
可能具有的任何其他上下文。它可能只是在顶层。但如果它处于最高水平,那么对于好或坏的暗示是它被多个程序多次使用,这就是它具有顶级范围的原因。如果它处于最高级别并不是问题 - 它不应该看起来像它,因为我们(或者不那么自以为是,我会#34;)考虑范围的方式。
我有理由相信,标准做法不是将一个程序放在范围链的上方,而不是因为这个杂乱而需要精确的,但事实上,范围大小往往会增加,这意味着在内部的层面上定义(比如三层深)代码的读者(我)需要考虑这个过程看到的整个范围,即使程序不需要它。这样说不是很好吗? &#34;啊,这是一个unlocal
块,它具有顶级需求及其自身参数所需的所有信息。&#34;
答案 0 :(得分:1)
对于为什么#%lambda-begin
应该存在,这是一个非常酷且有趣的案例。 (我一直在推动它。)
无论如何,到目前为止,我处理这个问题的方法是在函数外部定义元素,而在定义内部。以上面的事实为例:
(define fib
(let ()
(define (f index n-2 n-1)
(if (< index 1)
n-1
(f (sub1 index) n-1 (+ n-2 n-1))))
(lambda (n)
(f n 0 1)))
这仍然存在您必须输入(define fib ...)
而不是(define (fib ...) ...)
的问题,但它确实清楚地表明您正在做什么,并且它比您的{{1}更强大}。宏。
您可以做的另一件事是使用syntax-local-lift-expression
将功能提升到模块范围,使用卫生系统确保您可以多次使用相同的名称。整体宏看起来像:
define/free
现在,(require (for-syntax syntax/parse
syntax/parse/lib/function-header
racket/syntax))
(define-syntax (define/free stx)
(syntax-parse stx
[(_ f:function-header body ...)
(define f*
(syntax-local-lift-expression
#'(let ()
(define f body ...)
f.name)))
#`(define f.name #,f*)]))
已被完全取消当前上下文并给出了一个唯一的名称。然后,f
的原始绑定只会映射到新名称f
。
因此,您f
示例有效:
fib
但如果您尝试在(define (fib n)
(define/free (f index n-2 n-1)
(if (< index 1)
n-1
(f (sub1 index) n-1 (+ n-2 n-1))))
(f n 0 1))
中使用n
,则会收到未绑定的标识符错误:
f
(您也可以通过raise-syntax-error
,with-handlers
和exn:fail:syntax?
捕获错误并提出更好的错误,但这完全是另一个问题。)