在Racket中根据当前模块路径动态地要求一个模块(或如何在Racket中找到当前模块路径)

5
如果我想在运行时可选地要求一个模块,我可以使用 [dynamic-require'1。如果我想要引用已安装的包,这种方法非常有效:
(dynamic-require 'racket/match 'match)

如果我已经安装了 racket/match,那么这将需要它并且会评估该库中的 match 绑定。

然而,当我想要引用一个本地未安装的模块时,就会遇到问题。假设我有一个名为 eat.rkt 的模块,它提供了一个函数:bite

#lang racket ;; eat.rkt
(provide bite)
(define (bite) "Nom nom")

现在假设我们想要创建一个名为lunch.rkt的模块,该模块需要eat.rkt并调用该函数。此外,假设它们位于同一目录中:
#lang racket ;; lunch.rkt
(require "eat.rkt")
(bite) ; => Nom Nom

因为我使用了静态的 require,所以这很好,但是当我想要进行动态 require 时,这就会出问题:

#lang racket ;; lunch2.rkt
(define bite (dynamic-require "eat.rkt" 'bite)
(bite) ; => Nom Nom

虽然这看起来没问题,但事实证明,dynamic-require 所需的模块并不是基于模块路径要求的,而是根据current-directory。因此,如果我在定义该模块的目录中运行程序,则可以正常运行,但如果我在另一个目录中则会出现问题:

$ racket lunch2.rkt
"Nom Nom"
$ cd snacks/
$ racket ../lunch2.rkt
; default-load-handler: cannot open module file

显然,如果我知道这个模块的路径,我可以简单地将current-directory更改为此模块的目录。但是,如果我不知道这个模块的路径,有没有办法获取它?或者更直接地说,是否可能相对于要求的模块路径dynamic-require一个模块?

2个回答

5

define-runtime-path 表单定义了一个在运行时可用且与 current-directory 无关的路径。使用它来动态定义所需模块的路径,例如:

#lang racket
(require racket/runtime-path)
(define-runtime-path eat "eat.rkt")
(dynamic-require eat 'bite)

4

动态地引入一个模块,相对于当前模块路径(即模块保存的路径),最简单的方法是获取该模块路径并将其附加到你的相对模块。

你可以使用#%variable-referencevariable-reference->module-path-index来实现此操作。(你可能还想在其他情况下使用variable-reference->resolved-module-path,但我们不会在这里介绍它。)这两个函数的组合为我们提供了一个指向正在定义的模块的module-path-index?。 (或者一般来说,指向#%variable-reference所在的模块。)

因此,我们可以声明一个变量:

(define here (variable-reference->module-path-index (#%variable-reference)))

现在我们只需要将这里的路径与我们想要引用的模块的相对路径组合起来。如果你愿意,我们正在寻找构建路径的模块路径类比。
事实证明,我们要找的函数是:module-path-index-join,它接受一个基础路径和一个相对路径,并将它们拼接在一起。结果会看起来像这样:
(module-path-index-join "eat.rkt" here)

是的,与build-path预期的相反,但对于此函数来说,基本路径在第二个位置。

生成的模块lunch3.rkt如下:

#lang racket
(define here (variable-reference->module-path-index (#%variable-reference)))
(define bite (dynamic-require (module-path-index-join "eat.rkt" here) 'bite))

现在,lunch3.rkt将相对于其定义的位置而不是基于current-directory要求eat.rkt

$ racket lunch3.rkt
"Nom Nom"
$ cd snacks/
$ racket ../lunch3.rkt
"Nom Nom"

感谢Matthew Flatt帮助回答这个问题。


1
还有define-runtime-module-path-index,(如果我没记错的话)它也与raco distribute和其它相关。 - Ryan Culpepper
可以确认,define-runtime-module-path-index非常好用。 也许是在这个问题之后添加的? - soegaard

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接