我试图用Racket编写一个模块元语言mylang,它接受将修改的主体传递给的第二种语言,这样:
(module foo mylang typed/racket body)相当于:
(module foo typed/racket transformed-body)当然,可以用任何其他模块语言替换typed/racket部件。
我尝试了一个简单的版本,使身体保持不变。它可以很好地运行在命令行上,但是在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!以下是整个代码:
#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)需求(即我宁愿避免的解决方案):
(require (only-in typed/racket))模块中添加一个mylang可以完成这项工作,但我对一个通用解决方案很感兴趣,在这个解决方案中,mylang不需要了解al上的typed/racket (也就是说,如果有人添加了一个新的语言foo,那么mylang就应该开箱即用)。require和re-provide it,就像这里所做的的技巧不感兴趣,因为这会改变到实际模块的路径(例如,main和test就失去了它们的特殊行为)。
它在编译时也比较慢,因为子模块获得探视和/或实例化的次数更多(通过编写(begin-for-syntax (displayln 'here))可以看出这一点,并对大型typed/racket程序产生了明显的影响。ann、+和Number到typed/racket/base的箭头,则额外积分。发布于 2016-06-25 19:15:46
您可以做的一件事,我认为这不违反您的要求,就是把它放在一个模块中,充分展开该模块,然后匹配到#%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)如果你想以某种方式改变身体,你可以在膨胀之前或之后这样做。
插入额外(#%require lng)的模式匹配是必要的,因为在lng可用的上下文中扩展模块主体是不够的。将mod-body代码从module表单中删除意味着绑定将引用lng,但lng在运行时将不可用。这就是为什么我在没有它的情况下得到require: namespace mismatch; reference to a module that is not available错误的原因,这也是为什么需要在扩展后添加它。
从评论中更新
然而,正如@GeorgesDupéron在评论中指出的那样,这带来了另一个问题。如果lng提供了一个标识符x,并且使用它的模块导入了一个不同的x,那么就会出现一个不应该存在的导入冲突。要求行应该在模块语言的“嵌套作用域”中,这样它们就可以在这里隐藏像x这样的标识符。
@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))]))])))https://stackoverflow.com/questions/37867859
复制相似问题