[英]Module meta-language in Racket
I'm trying to write in Racket a module meta-language mylang
, which accepts a second language to which is passes the modified body, such that: 我正在尝试在Racket中编写模块元语言mylang
,该模块接受将修改后的主体传递给的第二种语言,例如:
(module foo mylang typed/racket body)
is equivalent to: 等效于:
(module foo typed/racket transformed-body)
where the typed/racket
part can be replaced with any other module language, of course. 当然,可以用任何其他模块语言替换typed/racket
部件。
I attempted a simple version which leaves the body unchanged. 我尝试了一个简单的版本,使主体保持不变。 It works fine on the command-line , but gives the following error when run in DrRacket: 它在命令行上可以正常工作,但是在DrRacket中运行时会出现以下错误:
/usr/share/racket/pkgs/typed-racket-lib/typed-racket/typecheck/tc-toplevel.rkt:479:30: require: namespace mismatch;
reference to a module that is not available
reference phase: 1
referenced module: "/usr/share/racket/pkgs/typed-racket-lib/typed-racket/env/env-req.rkt"
referenced phase level: 0 in: add-mod!
Here's the whole code: 这是整个代码:
#lang racket
(module mylang racket
(provide (rename-out [-#%module-begin #%module-begin]))
(require (for-syntax syntax/strip-context))
(define-syntax (-#%module-begin stx)
(syntax-case stx ()
[(_ lng . rest)
(let ([lng-sym (syntax-e #'lng)])
(namespace-require `(for-meta -1 ,lng-sym))
(with-syntax ([mb (namespace-symbol->identifier '#%module-begin)])
#`(mb . #,(replace-context #'mb #'rest))))])))
(module foo (submod ".." mylang) typed/racket/base
(ann (+ 1) Number))
(require 'foo)
Requirements (ie solutions I'd rather avoid): 要求(即我宁愿避免的解决方案):
(require (only-in typed/racket))
inside the mylang
module makes this work, but I'm interested in a general solution, where mylang
does not need to know about typed/racket
at al (ie if somebody adds a new language foo
, then mylang
should work with it out of the box). 在mylang
模块中添加一个(require (only-in typed/racket))
mylang
(require (only-in typed/racket))
可以完成这项工作,但是我对通用解决方案感兴趣,在这种解决方案中, mylang
不需要知道al的typed/racket
mylang
(即,如果有人添加了新语言foo
,则mylang
应该可以立即使用它)。 Also, I'm not interested in tricks which declare a submodule and immediately require
and re- provide
it, as is done here , because this changes the path to the actual module (so main
and test
loose their special behaviour, for example). 同样,我对那些声明一个子模块并立即require
并重新provide
它的技巧(如此处所做的那样)不感兴趣,因为这会更改到实际模块的路径(例如, main
和test
释放其特殊行为)。
It is also slower at compile-time, as submodules get visited and/or instantiated more times (this can be seen by writing (begin-for-syntax (displayln 'here))
, and has a noticeable impact for large typed/racket
programs. 它在编译时也较慢,因为子模块被访问和/或实例化的次数更多(这可以通过编写(begin-for-syntax (displayln 'here))
看到)看到,并且对于大型typed/racket
程序具有明显的影响。 。
Bonus points if the arrows in DrRacket work for built-ins provided by the delegated-to language, eg have arrows from ann
, +
and Number
to typed/racket/base
, in the example above. 如果DrRacket中的箭头适用于委托语言提供的内置组件,则奖励积分,例如,在上面的示例中,箭头具有从ann
, +
和Number
到typed/racket/base
箭头。
One thing you can do, which I don't think violates your requirements, is put it in a module, fully expand that module, and then match on the #%plain-module-begin
to insert a require. 您可以做的一件事情(我认为没有违反您的要求)是将它放在模块中,完全扩展该模块,然后在#%plain-module-begin
上匹配以插入需求。
#lang racket
(module mylang racket
(provide (rename-out [-#%module-begin #%module-begin]))
(define-syntax (-#%module-begin stx)
(syntax-case stx ()
[(_ lng . rest)
(with-syntax ([#%module-begin (datum->syntax #f '#%module-begin)])
;; put the code in a module form, and fully expand that module
(define mod-stx
(local-expand
#'(module ignored lng (#%module-begin . rest))
'top-level
(list)))
;; pattern-match on the #%plain-module-begin form to insert a require
(syntax-case mod-stx (module #%plain-module-begin)
[(module _ lng (#%plain-module-begin . mod-body))
#'(#%plain-module-begin
(#%require lng)
.
mod-body)]))])))
;; Yay the check syntax arrows work!
(module foo (submod ".." mylang) typed/racket/base
(ann (+ 1) Number))
(require 'foo)
And if you wanted to transform the body in some way, you could do that either before or after expansion. 而且,如果您想以某种方式变换身体,则可以在扩展之前或之后进行。
The pattern-matching to insert the extra (#%require lng)
is necessary because expanding the module body in a context where lng
is available isn't enough. 必须进行模式匹配以插入额外的(#%require lng)
,因为在lng
可用的上下文中扩展模块主体是不够的。 Taking the mod-body
code back out of the module
form means that the bindings will refer to lng
, but lng
won't be available at run-time. 将mod-body
代码从module
形式中取出意味着绑定将引用lng
,但是lng
在运行时将不可用。 That's why I get the require: namespace mismatch; reference to a module that is not available
这就是为什么我得到require: namespace mismatch; reference to a module that is not available
的原因require: namespace mismatch; reference to a module that is not available
require: namespace mismatch; reference to a module that is not available
error without it, and that's why it needs to be added after expansion. require: namespace mismatch; reference to a module that is not available
错误require: namespace mismatch; reference to a module that is not available
,这就是为什么在扩展后需要添加require: namespace mismatch; reference to a module that is not available
。
However, as @GeorgesDupéron pointed out in a comment, this introduces another problem. 但是,正如@GeorgesDupéron在评论中指出的那样,这引入了另一个问题。 If lng
provides an identifier x
and the module where it is used imports a different x
, there will be an import conflict where there shouldn't be. 如果lng
提供了一个标识符x
并且使用该标识符的模块导入了一个不同的x
,那么本不应该存在的导入冲突。 Require lines should be in a "nested scope" with respect to the module language so that they can shadow identifiers like x
here. 要求行应相对于模块语言位于“嵌套范围”内,以便它们可以在此处遮蔽x
标识符。
@GeorgesDupéron found a solution to this problem in this email on the racket users list , using (make-syntax-introducer)
on the mod-body
to produce the nested scope. @GeorgesDupéron 在球拍用户列表中的此电子邮件中找到了解决此问题的解决方案,使用mod-body
上的(make-syntax-introducer)
来生成嵌套范围。
(module mylang racket
(provide (rename-out [-#%module-begin #%module-begin]))
(define-syntax (-#%module-begin stx)
(syntax-case stx ()
[(_ lng . rest)
(with-syntax ([#%module-begin (datum->syntax #f '#%module-begin)])
;; put the code in a module form, and fully expand that module
(define mod-stx
(local-expand
#'(module ignored lng (#%module-begin . rest))
'top-level
(list)))
;; pattern-match on the #%plain-module-begin form to insert a require
(syntax-case mod-stx (module #%plain-module-begin)
[(module _ lng (#%plain-module-begin . mod-body))
#`(#%plain-module-begin
(#%require lng)
.
#,((make-syntax-introducer) #'mod-body))]))])))
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.