需要 Racket 中模块未提供的标识符

Require identifiers not provided by a module in Racket

假设我有一些文件 a.rkt:

#lang racket
(define a 12)

我现在想写一些测试用例,使用需要 a.rkt:

的文件 b.rkt
#lang racket
(require "a.rkt")
a

有什么方法可以让我 b.rkt 识别 a.rkt 中定义的标识符,而不必从第一个文件中 provide 它? (理想情况下根本不必更改第一个文件。)

我在 the documentation for require/provide 中没有立即看到任何内容。

您可以在 b.rkt 中使用 require/expose 来访问 a.rkt 中的绑定。 b.rkt 看起来像这样:

#lang racket
(require rackunit)
(require/expose "a.rkt" (a))
a

正如 Leif 所提到的,RackUnit 的 require/expose 允许在其他模块中使用未提供的标识符,但 its own documentation 不保证非常有力的保证:

Note that require/expose can be a bit fragile, especially when mixed with compiled code. Use at your own risk!

另一种方法是使用 submodules,它可以有效地提供一种认可的方式来导出私有 API 以用于测试或其他方式。

例如,假设一个模块实现了一个函数来测试字符串是否包含单个单词:

#lang racket

(provide word?)

(define (word? str)
  (not (ormap space? (string->list str))))

(define (space? c)
  (eq? c #\space))

(这也许不是最现实的例子,但请耐心等待。)

测试 space? 函数以确保其正常工作可能很有用,但它可能不应该是 public API 的一部分。要创建 "escape hatch",可以定义导出此绑定的子模块:

(module+ for-testing
  (provide space?))

名称 for-testing 是任意的——它可以是任何东西。无论哪种方式,现在都可以要求另一个模块中的子模块访问私有绑定:

#lang racket

(require rackunit
         (submod "a.rkt" for-testing))

(check-true (space? #\space))
(check-false (space? #\a))

这是一种更安全的方法,可以在不将标识符暴露给所有消费者的情况下从模块中暴露标识符。