我正在尝试在 Racket 中定义一种新语言,我们称之为 wibble。 Wibble 将允许加载模块,因此它必须将其表单转换为 Racket 需要的表单。但是在语言扩展中使用时,我无法让 require 工作。我最终将我的问题归结为以下奇怪的行为。
这是我的阅读器,它重新定义了 read
和 read-syntax
=== wibble/lang/reader.rkt ===
#lang racket/base
(provide (rename-out (wibble-read read) (wibble-read-syntax read-syntax)))
(define (wibble-read in)
(wibble-read-syntax #f in))
(define (wibble-read-syntax src in)
#`(module #,(module-name src) wibble/lang
#,@(read-all src in)))
(define (module-name src)
(if (path? src)
(let-values (((base name dir?) (split-path src)))
(string->symbol (path->string (path-replace-suffix name #""))))
'anonymous-module))
(define (read-all src in)
(let loop ((all '()))
(let ((obj (read-syntax src in)))
(if (eof-object? obj)
(reverse all)
(loop (cons obj all))))))
这是我非常简化的语言模块,它介绍了
(require racket/base)
进入每个摆动模块=== wibble/lang.rkt ===
#lang racket/base
(require (for-syntax racket/base))
(provide (rename-out (wibble-module-begin #%module-begin)) #%app #%datum #%top)
(define-syntax wibble-module-begin
(lambda (stx)
(syntax-case stx ()
((_ x ...) #`(#%module-begin (require #,(datum->syntax stx 'racket/base)) x ...)))))
使用上面的代码,那么这个 wibble 代码“有效”,即没有错误
#lang wibble
(cons 1 2)
(cons 3 4)
但以下
#lang wibble
(cons 1 2)
给出错误信息
cons: unbound identifier in module in: cons
真的,我只是在寻找关于发生了什么的解释。我确定差异与 Racket 文档(Racket Reference 3.1)有关
If a single form is provided, then it is partially expanded in a module-begin context. If the expansion leads to #%plain-module-begin, then the body of the #%plain-module-begin is the body of the module. If partial expansion leads to any other primitive form, then the form is wrapped with #%module-begin using the lexical context of the module body; this identifier must be bound by the initial module-path import, and its expansion must produce a #%plain-module-begin to supply the module body. Finally, if multiple forms are provided, they are wrapped with #%module-begin, as in the case where a single form does not expand to #%plain-module-begin.
但即使如此,我也不明白为什么只有一个表格会有什么不同,这似乎与部分扩展的时间有关,但我不太确定。我也不明白为什么 Racket 将单个表单视为特例。
顺便说一句,我可以通过对我的读者稍作修改来解决这个问题
(define (wibble-read-syntax src in)
#`(module #,(module-name src) wibble/lang
#,@(read-all src in) (void)))
硬编码
(void)
形式意味着我总是有不止一种形式,而且一切都有效。抱歉,这篇文章很长,我只是想了解一下这些东西是如何工作的。
最佳答案
好吧,我想我已经想通了。
您的直觉是正确的,因为问题在于单一形式模块体的部分扩展的时间。在您的reader.rkt
内文件,您生成一个 (module ...)
形式。正如您的问题所引用的摘录所述,forms ...
然后对其中的一部分进行特殊处理,因为只有一个。让我们看一下部分扩展文档的摘录:
As a special case, when expansion would otherwise add an
#%app
,#%datum
, or#%top
identifier to an expression, and when the binding turns out to be the primitive#%app
,#%datum
, or#%top
form, then expansion stops without adding the identifier.
我几乎可以肯定,此时发生的部分扩展对
cons
有影响。标识符。这是我仍然不确定的部分......我的直觉告诉我,正在发生的事情是部分扩展正在尝试找到 cons
的绑定(bind)。标识符(因为它是括号的第一部分,标识符可以绑定(bind)到应该扩展的宏,因此需要检查)但不能,所以它会发脾气。请注意,即使 cons
没有阶段 1(语法扩展时间)绑定(bind),宏扩展器仍然希望标识符有阶段 0(运行时)绑定(bind)(除其他外,这有助于扩展器保持卫生)。因为所有这些部分扩展都发生在您的 (module ...)
的主体上。表格(在您注入(inject) (#%module-begin ...)
表格的 (#%require ...)
表格之前完成),cons
在扩展过程中没有约束力,所以扩展,我相信,失败。然而,一个天真的解决你的问题是重写
wibble-read-syntax
如下:(define (wibble-read-syntax src in)
(let* ((read-in (read-all src in))
(in-stx (and (pair? read-in) (car read-in))))
#`(module #,(module-name src) wibble/lang
(require #,(datum->syntax in-stx 'racket/base))
#,@read-in))
然后您可以删除
(#%require ...)
来自您的 (#%module-begin ...)
的表格宏。但是,在我看来,这不是解决问题的最佳方法。出于清洁考虑,在
require
中进行硬编码就像你在 wibble/lang.rkt
中所做的那样会make Eli Barzilay and co. cry .更简单的方法是更新您的 lang.rkt
。文件到这样的东西:=== wibble/lang.rkt ===
#lang racket/base
(require (for-syntax racket/base))
(provide (rename-out (wibble-module-begin #%module-begin))
(except-out (all-from-out racket/base) #%module-begin #%app #%datum #%top)
#%app #%datum #%top)
(define-syntax wibble-module-begin
(lambda (stx)
(syntax-case stx ()
((_ x ...) #`(#%module-begin x ...)))))
以这种约定编写就不需要任何硬编码
(require ...)
形成并防止像您发现的那样的细微错误发生。如果您不明白为什么会这样,请记住您已经提供了 #%module-begin
使用此文件的标识符,随后将其绑定(bind)到所有 #lang wibble
文件。原则上,您可以以这种方式绑定(bind)的标识符没有限制。如果您想进一步阅读,这里有一个 blog post I wrote 的无耻自我广告。一会儿回到这个主题。我希望我有所帮助。
关于racket - 试图理解语言扩展中的需求,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31624384/