无法访问附录范围的内部定义

时间:2017-05-08 23:08:52

标签: racket

  

是否可以剥离对词法变量进行内部定义的访问?

也许这不是问题,但有时我希望我可以定义内部程序而无需访问封闭范围。考虑这个例子:

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

1 个答案:

答案 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-errorwith-handlersexn:fail:syntax?捕获错误并提出更好的错误,但这完全是另一个问题。)