简体   繁体   English

球拍中的模块元语言

[英]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): 要求(即我宁愿避免的解决方案):

  • Adding a (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它的技巧(如此处所做的那样)不感兴趣,因为这会更改到实际模块的路径(例如, maintest释放其特殊行为)。

    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+Numbertyped/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

Update from comments 来自评论的更新

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM