Racket:局部扩展递归定义
Racket: local-expanding recursive definitions
我正在尝试编写一个行为类似于 racket define 的宏,但以某种方式处理完全扩展的 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))]))
一切正常,除非满足递归定义:
(define/expand (sum n)
(if (<= n 0)
0
(+ n (sum (- n 1)))))
运行 它引发错误
sum: unbound identifier in module in: 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))]))
解决了问题(local expanded成功将procedure扩展成define-values
),但是创建了另外一个:
module: out-of-context identifier for definition in: sum
指向总和的定义。原因可能是扩展器将标识符绑定到 ctx
中的一个,而不是当前上下文中的 head
。
直觉上这似乎不是一个罕见的问题,但我无法通过网络找到解决方案。我认为我应该以某种方式使用 local-expand/capture-lifts
和 syntax-local-lift-expression
,但我不知道如何正确使用它。有人可以澄清发生了什么事吗and/or 提示如何修复它?
让我们在顶级 (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))]))
然后在回复中:
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
我正在尝试编写一个行为类似于 racket define 的宏,但以某种方式处理完全扩展的 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))]))
一切正常,除非满足递归定义:
(define/expand (sum n)
(if (<= n 0)
0
(+ n (sum (- n 1)))))
运行 它引发错误
sum: unbound identifier in module in: 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))]))
解决了问题(local expanded成功将procedure扩展成define-values
),但是创建了另外一个:
module: out-of-context identifier for definition in: sum
指向总和的定义。原因可能是扩展器将标识符绑定到 ctx
中的一个,而不是当前上下文中的 head
。
直觉上这似乎不是一个罕见的问题,但我无法通过网络找到解决方案。我认为我应该以某种方式使用 local-expand/capture-lifts
和 syntax-local-lift-expression
,但我不知道如何正确使用它。有人可以澄清发生了什么事吗and/or 提示如何修复它?
让我们在顶级 (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))]))
然后在回复中:
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