首页 文章

Racket:局部扩展的递归定义

提问于
浏览
2

我正在尝试编写一个像行星定义一样的宏行为,但是以某种方式处理完全扩展的球拍程序(为了简单起见,在下面的例子中):

(define-syntax (define/expand stx)
  (syntax-case stx ()
    [(_ (head args ...) body body-rest ...)
       (let* ([define/racket (syntax/loc stx (define (head args ...) body body-rest ...))]
              [fully-expanded (local-expand define/racket 'top-level (list))])
           fully-expanded)]
    [(_ id expr) (syntax/loc stx (define id expr))]))

除非满足递归定义,否则一切都很好:

(define/expand (sum n)
  (if (<= n 0)
      0
      (+ n (sum (- n 1)))))

运行它会引发错误

sum:模块中的未绑定标识符:sum

指向 sum 的调用(不是定义) . 显然,本地扩展器没有捕获 sum 的定义 . 我尝试了一种简单的方法来修复它:创建新的本地定义上下文并将 head 绑定到其中:

(define-syntax (define/expand stx)
  (syntax-case stx ()
    [(_ (head args ...) body body-rest ...)
       (let* ([ctx (syntax-local-make-definition-context)]          ; <- These two lines added 
              [_ (syntax-local-bind-syntaxes (list #'head) #f ctx)] ; <--/
              [define/racket (syntax/loc stx (define (head args ...) body body-rest ...))]
              [fully-expanded (local-expand define/racket 'top-level (list) ctx)])
           fully-expanded)]
    [(_ id expr) (syntax/loc stx (define id expr))]))

它解决了问题(本地扩展成功地将过程扩展为 define-values ),但创建了另一个:

module:用于定义的out-of-context标识符:sum

指向总和的定义 . 原因可能是扩展器将标识符绑定到 ctx 中的一个而不是当前上下文中的 head .

直觉上它似乎不是一个罕见的问题,但我找不到网络上的解决方案 . 我以为我应该以某种方式使用 local-expand/capture-liftssyntax-local-lift-expression ,但我不会't get how to use it properly. Could someone clarify what'继续和/或提示如何修复它?

1 回答

  • 2

    让我们在顶级(repl)尝试你的第一个程序:

    #lang racket
    (define-syntax (define/expand stx)
      (syntax-case stx ()
        [(_ (head args ...) body body-rest ...)
         (let*
             ([define/racket  (syntax/loc stx (define (head args ...) body body-rest ...))]
              [fully-expanded (local-expand define/racket 'top-level (list))])
           fully-expanded)]
        [(_ id expr)
         (syntax/loc stx (define id expr))]))
    

    然后在repl中:

    Welcome to DrRacket, version 6.6.0.3--2016-07-28(-/f) [3m].
    Language: racket, with debugging [custom]; memory limit: 1024 MB.
    > (define/expand (sum n)
      (if (<= n 0)
          0
          (+ n (sum (- n 1)))))
    .#<syntax:3:2 (define-values (sum) (lambda ...>
    > (sum 5)
    15
    

    这表明您的程序在顶级工作 .

    同一方法在模块上下文中不起作用的原因是 #%module-begin 在扩展表达式之前使用表单的部分扩展来检测定义 . 换句话说,define / expand必须告诉 #%module-begin 它扩展为 sum 的定义,但必须延迟使用 local-expand ,直到 #%module-begin 检测到模块级别的所有绑定标识符 .

    这表明了两步法:

    #lang racket
    
    (define-syntax (delay-expansion stx)
      (syntax-case stx ()
        [(_delay-expansion more ...)
         (let ([fully-expanded (local-expand #'(lambda () more ...) 'module (list))])
           (display fully-expanded)
           fully-expanded)]))
    
    (define-syntax (define/expand stx)
      (syntax-case stx ()
        [(_ (head args ...) body body-rest ...)
         (syntax/loc stx
           (define (head args ...)
             ((delay-expansion
               body body-rest ...))))]
        [(_ id expr)
         (syntax/loc stx
           (define id expr))]))
    
    
    (define/expand (sum n)
      (if (<= n 0)
          0
          (+ n (sum (- n 1)))))
    
    (sum 5)
    

    在这里查看更多:https://groups.google.com/d/msg/racket-users/RB3inP62SVA/54r6pJL0wMYJ

相关问题