AlbusMPiroglu
AlbusMPiroglu

Reputation: 653

finding out the list of required modules by a module in racket

I want to keep a list of required modules of a particular module (let's say the current-module).

I feel like there are some other options (such as parsing the module?) that could be tried, but I started playing with the idea of shadowing (require) and adding the required items to a hash-table with the module-name. The problem is I cannot figure how to write a syntax definition for it.

Although not working, a function definition equivalent would be like below:

(define require-list (make-hash))
(define require
  (lambda vals
    ; add vals to hash-table with key (current-namespace)
    (let ([cn (current-namespace)])
      (hash-set! require-list cn
                 (append vals (hash-ref require-list cn))))
    (require vals)))

.. it seems the last line call should be modified to call the original (require) as well?

A correct version or a pointer to how to do it, or any other way of achieving the original goal highly appreciated.

Upvotes: 1

Views: 136

Answers (2)

Alexis King
Alexis King

Reputation: 43852

If you just want to get a list of imports for a particular module, there is a convenient built-in called module->imports that will do what you want. It will return a mapping between phase levels and module imports—phase levels higher than 0 indicate imports used at compile-time for use in macro expansion.

> (require racket/async-channel)
> (module->imports 'racket/async-channel)
'((0
   #<module-path-index:(racket/base)>
   #<module-path-index:(racket/contract/base)>
   #<module-path-index:(racket/contract/combinator)>
   #<module-path-index:(racket/generic)>))

Note that the module in question must be included into the current namespace in order for module->imports to work, which require or dynamic-require will both do.

This will inspect the information known by the compiler, so it will find all static imports for a particular module. However, the caveats about dynamic requires mentioned by John Clements still apply: those can be dynamically performed at runtime and therefore will not be detected by module->imports.

Upvotes: 2

John Clements
John Clements

Reputation: 17203

Short short version:

Have you tried turning on the module browser?

Short version:

  • You're going to need a macro for this, and
  • It's not going to be a complete solution

The existing require is not a function; it's a language form, implemented as a macro. This is because the compiler needs to collect the same information you do, and therefore the required modules must be known at compile time.

The right way to do this--as you suggest--is definitely to leverage the existing parsing. If you expand the module and then walk the resulting tree, you should be able to find everything you need. The tree will be extremely large, but will contain (many instances of) a relatively small number of primitives, so writing this traversal shouldn't be too hard. There will however be a lot of fiddling involved in setting up namespace anchors etc. in order to get the expansion to happen in the first place.

Regarding your original idea: you can definitely create a macro that shadows require. You're going to want to define it in another file and rename it on the way out so that your macro can refer to the original require. Also, the require form has a bunch of interesting subforms, and coming up with a macro that tries to handle all of these subforms will be tricky. If you're looking at writing a macro, though, you're already thinking about an 80% solution, so maybe this won't bother you.

Finally: there are forms that perform dynamic module evaluation, and so you can't ever know for sure all of the modules that might be required, though you could potentially annotate these forms (or maybe shadow the dynamic module-loading function) to see these as they happen.

Also, it's worth mentioning that you may get more precise answers on the Racket mailing list.

Upvotes: 2

Related Questions