Require identifiers not provided by a module in Racket
Asked Answered
C

2

7

Let's say I have some file a.rkt:

#lang racket
(define a 12)

I now want to write some test cases, using the file b.rkt that requires a.rkt:

#lang racket
(require "a.rkt")
a

Is there any way I can get b.rkt to recognize the identifier defined in a.rkt without having to provide it from the first file? (Ideally without having to change the first file at all.)

I don't see anything immediately in the documentation for require/provide.

Cordage answered 6/12, 2015 at 20:44 Comment(2)
Neat idea for a self-answered question, but you might want to add more motivation in the question. I.e., clarify that this is for testing, not something you would want to do in general.Phalange
That's a good idea, I'll change that.Cordage
P
6

As Leif mentions, RackUnit's require/expose will allow using unprovided identifiers in other modules, but its own documentation doesn't promise a very strong guarantee:

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

Another approach would be to use submodules, which can effectively provide a sanctioned way to export a private API for use in tests or other means.

For example, consider a module that implements a function to test if a string contains a single word:

#lang racket

(provide word?)

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

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

(This is, perhaps, not the most realistic example, but bear with me.)

It might be useful to test the space? function to ensure it works, but it likely shouldn't be part of the public API. To create an "escape hatch", it's possible to define a submodule that exports this binding:

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

The name for-testing is arbitrary—it could be anything. Either way, it is now possible to require that submodule in another module to get access to the private bindings:

#lang racket

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

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

This is something of a safer way to expose identifiers from modules without exposing them to all consumers.

Phalange answered 6/12, 2015 at 21:27 Comment(1)
Sometimes, it's unfeasible to change the original source to add extra provides, e.g., students who do not follow directions. Dan Feltey's testing framework uses require/expose (and friends) in this manner: github.com/dfeltey/pdptest. (Not contradicting your answer, just describing another use case context.)Yogh
C
5

You can use require/expose in b.rkt to get access to the binding in a.rkt. b.rkt would look something like this:

#lang racket
(require rackunit)
(require/expose "a.rkt" (a))
a
Cordage answered 6/12, 2015 at 20:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.