racket

When will compile-time expressions be reevaluated in Racket?


This is one follow-up question of this QA answer. I use Racket v8.16.

For compile_time_instantiation.rkt from doc1:

(module compile-time-number racket/base
  (require (for-syntax racket/base))
  (begin-for-syntax
    (printf "picked ~a\n" (random)))
  (printf "running\n"))

Then:

$ racket -i -e '(dynamic-require "compile_time_instantiation.rkt" #f)'
Welcome to Racket v8.16 [cs].
picked 0.015421409441077423
running
$ racket -i -e '(require "compile_time_instantiation.rkt")'
Welcome to Racket v8.16 [cs].
picked 0.14007121863188537
running
picked 0.7200261945290138
$ racket -i
Welcome to Racket v8.16 [cs].
> (require "compile_time_instantiation.rkt")
picked 0.7451208846143317
running
$ racket compile_time_instantiation.rkt
picked 0.9883107842338839
running

doc1 says:

Meanwhile, dynamic-require only instantiates a module; it does not visit the module. That simplification is why some of the preceding examples use dynamic-require instead of require.

When a module is instantiated, the run-time expressions in its body are evaluated.

So the extra "picked ..." in the 2nd command is from the extra visit by require beyond that when expansion:

When a module is visited, the compile-time expressions (such as macro definition) in its body are evaluated.

As a module is expanded, it is visited.

The compile-time expressions of a module that are evaluated by visiting include both the right-hand sides of define-syntax forms and the body of begin-for-syntax forms.

But why does the 3rd not have that?


Solution

  • This probably has something to do with it: 16.3.4 Lazy Visits via Available Modules

    A top-level require of a module does not actually visit the module. Instead, it makes the module available. An available module will be visited when a future expression needs to be expanded in the same context. The next expression may or may not involve some imported macro that needs its compile-time helpers evaluated by visiting, but the module system proactively visits the module, just in case.

    $ racket -i
    Welcome to Racket v8.7 [cs].
    > (require "compile_time_instantiation.rkt")
    picked 0.2503381960723421
    running
    > (+ 1 1)
    picked 0.7182273015825262
    2
    > (+ 2 2)
    4
    

    If -i is left off the second command, "picked" only prints once. -i makes the REPL start up, which might be enough to trigger a visit of the module.

    $ racket -e '(require "compile_time_instantiation.rkt")'
    picked 0.5183696676559958
    running