[plt-scheme] Reloading code

From: Ryan Culpepper (ryanc at ccs.neu.edu)
Date: Sun Mar 2 01:20:24 EST 2008

Curt Ferguson wrote:
> Hi all.  I'm very new to scheme so I'm sure I'll have a number of
> questions.  I'll try to phrase them intelligently and clearly.
> 
> What I'm trying to do is create a program that is basically an event
> loop.  It will read in various modules and then start the loop, the loop
> will perform various processing tasks and look for user input, without
> any input it will run various tasks again and look for input...etc.
> 
> What I need is for a user to input a command and have the program reload
> parts of the program code, not necessarily all of it.  Probably
> individual modules.   I don't know if it would be possible for an
> internal command to reload individual procedures.  Purpose being if/when
> a source file is modified on disk, I want the user (who won't have
> access to the machine shell) to be able to reload just the modified
> portion of the code.  Or as small a section as possible that includes
> the modified section, such as a module perhaps.
> 
> I know this is probably an extended topic to get answered by mail, so
> what I'm asking the list for is your reference links that might be
> pertinent...  Snippets, examples, modules, etc, that would show me
> things like this. (Or any examples of reloading code at all).

It's possible to re-declare modules. If those modules are participating 
in a program (that is, they've been invoked), then when you redeclare 
the module, the new version is invoked and its exports override the 
previous exports. For this to happen, you'll need to set the 
'compile-enforce-module-constants' parameter to #f before you load any 
module that you may eventually want to reload. See the section of the 
reference on "Module Re-declarations".

One main caveat (among several caveats I could list): If you reload a 
module that declares structs, then you'll generate entirely new 
structure types and you'll redefine the names of the constructors, 
predicates, and accessors to work only on the new versions. If you have 
instances of the old structure types floating around, things will get weird.

Consider whether you really want to reload modules the way you 
described. You might be able to get what you want by setting up your 
code to use parameters or boxes or whatnot and updating those when you 
want to change the behavior of your program.

**

Here's the rough path of control in the normal loading of modules:

    'require' (or 'dynamic-require')
-> the module name resolver (see 'current-module-name-resolver')
-> the loader (see 'current-load')
-> the evaluator (see 'current-eval')

For a module to be invoked (required), it must first be declared. The 
module name resolver is responsible for loading module declarations on 
demand from the filesystem. The module name resolver keeps a table of 
modules that have been loaded. It only loads the declaration the first 
time around; subsequent requests for the module just get the existing 
declaration, even if the file has changed.

What you want to do is to bypass the module name resolver and reload the 
module declarations directly. To do this, though, you need to use some 
of the same machinery as the module name resolver to make sure you're 
redeclaring the right thing. The section on "Module Names and Loading" 
seems to be the best reference for this.

Here's some code that should give you a starting point. The reloading 
capabilities are in the "manager.ss" module. It uses 'dynamic-require' 
to start off the managed program, which consists of "value.ss" and 
"print.ss".

;; manager.ss

#lang scheme/base
(provide reload
          go)

(compile-enforce-module-constants #f)

(define (reload x)
   (let ([resolved-x ((current-module-name-resolver) x #f #f #f)])
     (parameterize ((current-module-declare-name resolved-x))
       (load/use-compiled (resolved-module-path-name resolved-x)))))

(define go (dynamic-require "print.ss" 'print-the-value))

;; end of manager.ss

;; print.ss

#lang scheme/base
(require "value.ss")
(provide print-the-value)

(define (print-the-value)
   (printf "~a\n" the-value))

;; end of print.ss

;; value.ss

#lang scheme/base
(provide the-value)

(define the-value 'apple)

;; end of value.ss

Then:

 > (require "manager.ss")
 > (go)
apple

Now edit "value.ss" and change 'apple' to 'orange'.

 > (reload "value.ss")
 > (go)
orange

Hope that helps,
Ryan

> _________________________________________________
>   For list-related administrative tasks:
>   http://list.cs.brown.edu/mailman/listinfo/plt-scheme



Posted on the users mailing list.