我正在尝试用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)
要求(即我宁愿避免的解决方案):
- 在
mylang
模块内添加(require (only-in typed/racket))
可以使其工作,但我对通用解决方案感兴趣,其中mylang
不需要知道typed/racket
(即如果有人添加了一个新的语言foo
,那么mylang
应该可以直接使用它)。 此外,我不感兴趣的方法是声明一个子模块并立即
require
和重新provide
它,就像这里做的那样,因为这会改变实际模块的路径(例如,main
和test
将失去其特殊行为)。这也会在编译时变慢,因为子模块被访问和/或实例化更多次(可以通过编写
(begin-for-syntax (displayln 'here))
来看到这一点,并且对于大型的typed/racket
程序有明显的影响。如果DrRacket中的箭头适用于委托给语言提供的内置函数,例如在上面的示例中从
ann
、+
和Number
到typed/racket/base
的箭头,则会获得额外的奖励分数。