2017-05-08 122 views
2

Can access to lexical variables be stripped for internal definitions?无法访问范围内部定义

也许这是一个没有问题,但有时我希望我可以定义内部过程,而无需访问范围。考虑下面这个例子:

(define (usual-racket n) 
    (define (hi a) 
    (displayln n)) 
    (hi 'hi) 
    n) 

我在这里“不小心”在内部定义的过程hi类型n而不是a。因为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 (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可能有。它可能只是在顶层。但是,如果它处于最高级别,那么对于好的或者不好的含义就是它在多个过程中被多次使用,这就是它具有它所具有的顶级范围的原因。如果它处于最高水平,这不是问题,因为我们(或者更不在意“我”)考虑范围的方式不应该是这样。

我相当肯定地认为,标准实践并不是将一个过程放在范围链的上方,而是因为这个混乱而恰恰需要,但实际上范围的大小只会趋于增加,这意味着在内部定义(比如三个深度级别),代码读者(我)需要考虑这个过程所看到的整个范围,即使过程不需要它。这样说不是很好吗? “啊,这是一个unlocal区块,它具有它从顶层需求和它自己的论点所需的所有信息。”

+0

有趣!首先,这是一个简单的问题;你为什么不直接在顶层定义你的内部函数(在这里是'hi')?我认为这个问题的答案可能有助于澄清你正在寻找的设备的细节。 –

+0

@JohnClements对我来说顶级定义会让人产生很多精神混乱。代码位置说明了代码的一些内容。顶级代码是所有人都可以看到的,并且可能随时需要了解一些过程。一般来说,你没有内部定义的问题,它们只能在闭包中泄漏出来。所以我的问题与此相反,以防止泄漏。 –

回答

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 ...) ...)这个问题,但它确实非常清楚自己在做什么,它比你多了几分稳健define/free宏。

您可以做的另一件事是使用syntax-local-lift-expression将功能提升到模块范围,使用卫生系统确保您可以多次使用相同的名称。宏观整体看起来是这样的:

(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)) 

但是,如果你尝试内f使用n,你会得到一个未绑定的标识符错误:

> (define (fib n) 
    (define/free (f index n-2 n-1) 
     (displayln n) 
     (if (< index 1) 
      n-1 
      (f (sub1 index) n-1 (+ n-2 n-1)))) 
    (f n 0 1)) 

n: identifier used out of context 
context...: 
matching binding...: in: n 

(你也可以赶上错误并且用raise-syntax-error,with-handlersexn:fail:syntax?提出更好的一个,但这是完全不同的问题。)

+0

最后的语法quasiquote需要'(define f.name#,f *)'。我自己编辑它,但作为一个4字符的编辑它不会让我。非常感谢,'syntax-local-lift-expression'命中了现场! –

+0

哎呀,对不起,我会解决这个问题,谢谢。 –