Racket中的模块元语言

Suz*_*ron 8 metalanguage require racket

我正在尝试在Racket中编写一个模块元语言mylang,它接受第二种语言传递修改后的正文,这样:

(module foo mylang typed/racket body)
Run Code Online (Sandbox Code Playgroud)

相当于:

(module foo typed/racket transformed-body)
Run Code Online (Sandbox Code Playgroud)

当然,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!
Run Code Online (Sandbox Code Playgroud)

这是整个代码:

#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)
Run Code Online (Sandbox Code Playgroud)

要求(即我宁愿避免的解决方案):

  • (require (only-in typed/racket))mylang模块内部添加一个可以使这个工作,但我对一般解决方案感兴趣,在那里mylang不需要知道typed/racketal(即如果有人添加一种新语言foo,那么mylang应该开箱即用).
  • 另外,我没有兴趣在声明子模块,并立即技巧require和重新provide它,因为都是在这里完成的,因为这改变了路径实际模块(所以maintest失去其特殊的行为,例如).

    它在编译时也比较慢,因为子模块被访问和/或实例化的次数更多(这可以通过编写来看出(begin-for-syntax (displayln 'here)),并且对大型typed/racket程序有明显的影响.

  • 奖励积分,如果在DrRacket工作的箭头由委派到语言,如提供内置插件具有箭头ann,+Numbertyped/racket/base上面的例子.

Ale*_*uth 4

您可以做的一件事(我认为这不会违反您的要求)是将其放入模块中,完全扩展该模块,然后匹配以#%plain-module-begin插入需求。

\n\n
#lang racket\n\n(module mylang racket\n  (provide (rename-out [-#%module-begin #%module-begin]))\n  (define-syntax (-#%module-begin stx)\n    (syntax-case stx ()\n      [(_ lng . rest)\n       (with-syntax ([#%module-begin (datum->syntax #f \'#%module-begin)])\n         ;; put the code in a module form, and fully expand that module\n         (define mod-stx\n           (local-expand\n            #\'(module ignored lng (#%module-begin . rest))\n            \'top-level\n            (list)))\n         ;; pattern-match on the #%plain-module-begin form to insert a require\n         (syntax-case mod-stx (module #%plain-module-begin)\n           [(module _ lng (#%plain-module-begin . mod-body))\n            #\'(#%plain-module-begin\n                (#%require lng)\n                .\n                mod-body)]))])))\n\n;; Yay the check syntax arrows work!\n(module foo (submod ".." mylang) typed/racket/base\n  (ann (+ 1) Number))\n\n(require \'foo)\n
Run Code Online (Sandbox Code Playgroud)\n\n

如果你想以某种方式改变身体,你可以在扩张之前或之后进行。

\n\n

插入额外内容的模式匹配(#%require lng)是必要的,因为在可用的上下文中扩展模块主体lng是不够的。mod-body从表单中取出代码意味着module绑定将引用lng,但lng在运行时不可用。这就是为什么我在require: namespace mismatch; reference to a module that is not available没有它的情况下收到错误,这就是为什么需要在扩展后添加它。

\n\n

根据评论更新

\n\n

然而,正如 @GeorgesDup\xc3\xa9ron 在评论中指出的那样,这引入了另一个问题。如果lng提供了一个标识符x,并且使用它的模块导入了一个不同的x,那么就会出现不应该出现的导入冲突。要求行应该位于相对于模块语言的“嵌套范围”中,以便它们可以像x这里一样隐藏标识符。

\n\n

@GeorgesDup\xc3\xa9ron 在球拍用户列表的这封电子邮件中找到了此问题的解决方案,使用(make-syntax-introducer)mod-body生成嵌套范围。

\n\n
(module mylang racket\n  (provide (rename-out [-#%module-begin #%module-begin]))\n  (define-syntax (-#%module-begin stx)\n    (syntax-case stx ()\n      [(_ lng . rest)\n       (with-syntax ([#%module-begin (datum->syntax #f \'#%module-begin)])\n         ;; put the code in a module form, and fully expand that module\n         (define mod-stx\n           (local-expand\n            #\'(module ignored lng (#%module-begin . rest))\n            \'top-level\n            (list)))\n         ;; pattern-match on the #%plain-module-begin form to insert a require\n         (syntax-case mod-stx (module #%plain-module-begin)\n           [(module _ lng (#%plain-module-begin . mod-body))\n            #`(#%plain-module-begin\n                (#%require lng)\n                .\n                #,((make-syntax-introducer) #\'mod-body))]))])))\n
Run Code Online (Sandbox Code Playgroud)\n