dynamic-require a module with respect to the current module path in Racket (Or how to find the current module path in Racket)
Asked Answered
T

2

5

If I want to optionally require a module at runtime, I can use [dynamic-require'1. This works great if I want to require a package that has been installed, for example:

(dynamic-require 'racket/match 'match)

This will (provided I have racket/match installed), require racket/match and evaluate to the match binding in that library.

However, I run into trouble when I want to require a local, non installed module. Let's say I have some module called eat.rkt, which provides one function: bite:

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

Now lets say we want to make a lunch.rkt module that requires eat.rkt, and calls the function. Furthermore, lets suppose I put them in the same directory:

#lang racket ;; lunch.rkt
(require "eat.rkt")
(bite) ; => Nom Nom

This is fine because I used static require, but this falls apart when I want to do a dynamic-require:

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

While this appears to be fine, it turns out that the module required by dynamic-require is NOT required based on the module's path, but on current-directory. So, if I run the program in the directory the module is defined, that's fine, but if I'm in another directory, everything breaks:

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

Obviously I could just change the current-directory to this module's directory if I know where it is. But if I don't know this module's path, is there any way to get it? Or, more directly, is it possible to dynamic-require a module relative to the requiring's module path?

Teodora answered 6/3, 2017 at 18:21 Comment(0)
I
5

The define-runtime-path form defines a path that will be available at runtime and is independent of the current-directory. Use it to define the path to the module you want to require dynamically, for example:

#lang racket
(require racket/runtime-path)
(define-runtime-path eat "eat.rkt")
(dynamic-require eat 'bite)
Instant answered 26/2, 2018 at 19:30 Comment(0)
T
4

The easiest way to dynamic-require a module relative to the current module path (which is to say the path where the module is saved), is to get that module path and append it your relative module.

You can do this with #%variable-reference and variable-reference->module-path-index. (You may also want to use variable-reference->resolved-module-path for other situations, but we will not do it here.) Composing these two functions gives us a module-path-index? to the module being defined. (Or in general, the module that the #%variable-reference came from.)

So, we can a variable like:

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

Now all that is left is to compose this here path with the relative path to the module we want to require. We are looking for the module path analogy of build-path, if you will.

It turns out that the function we are looking for is: module-path-index-join, which takes a base path and a relative path and appends them together. The result will look something like:

(module-path-index-join "eat.rkt" here)

(Yes, it is backwards of what you would expect from build-path, but the base path comes second for this function.)

The resulting module, lunch3.rkt looks like:

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

And now lunch3.rkt will require eat.rkt relative to where its defined, not based on the current-directory:

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

Thank you to Matthew Flatt for helping with this answer.

Teodora answered 6/3, 2017 at 18:34 Comment(2)
There's also define-runtime-module-path-index, which (IIRC) also cooperates with raco distribute and friends.Feininger
Can confirm, define-runtime-module-path-index works like a charm. Maybe it was added after this question?Cowpea

© 2022 - 2024 — McMap. All rights reserved.