[plt-scheme] conditional module inclusion and compilation

From: Eli Barzilay (eli at barzilay.org)
Date: Wed Sep 2 17:59:20 EDT 2009

On Sep  2, Lee Spector wrote:
> I've developed some code in DrScheme under Mac OS X that I also want
> to run sometimes under Linux -- it's computationally intensive stuff
> and I'll want to do development runs on my mac but then do lots of
> bigger runs on our Linux cluster.
> 
> I have some graphics in the Mac OS X version that I don't know how
> to produce in my command-line/text-based Linux environment, but I
> don't need the graphics there anyway. The graphics are just plots of
> progress which are useful when I'm developing and watching a run,
> but the cluster runs produce text files of data which are good
> enough. So to get the code working on the cluster I just removed
> "(require plot)" and all of the code that called anything from the
> plot module.
> 
> That worked but I don't want to have to do this kind of surgery on
> the code every time I move it to the cluster, which I'll be doing
> frequently. Ideally I'd like to be able to write my code in such a
> way that it knows where it's running and it includes the plot module
> and calls the plot code only if it's running somewhere
> appropriate. I do this in Common Lisp by using the #+: and #-:

(You mean #+ and #-, which are usually followed by a keyword...)

> conditional compilation syntax and/or by looking for things in the
> variable *features*, which includes symbols that describe the
> environment. Is there anything similar in PLT?

Not in that way.  More than that, the fact that there is no direct
equivalent of this is an intnetional way of avoiding the mess you can
easily get into with CL: `*features*' is a description of the
"currently running system" but it is used for three separate roles:
reading, compiling, and running.  For example, if you were using
something like:

  #+linux (require plot)

and then compile the code and run it on the linux machine, then you
*would* get it to run, since the reader already worked elsewhere.

Now, there is (of course) some way to get a similar facility (and you
can of course shoot your foot in various ways).  One way would be:

  (define-syntax (foo stx)
    (if (eq? 'unix (system-type))
      #'(printf "Running on a unix machine\n")
      #'(printf "Running on a non-unix machine\n")))

Having this kind of platform-dependent macro means that compiled files
are now platform-dependent too, so things will work as long as you're
careful to not move .zo files between machines.  (Note that you can do
the same with something like and environment variable and the `getenv'
function, with exactly the same kind of problem.)

A much better idea is to delay the decision to where it belongs more
naturally -- in the runtime.  But this runs into the problem you've
seen: `require' is not an expression, so you can't put it in an `if'.
The reason you can't do that is that `require' is used at compile-time
to tell the compiler how to compile and link the code (think about
requiring a module that provides some macro that you're using: you
*need* it at compile time then).

For such cases, there is `dynamic-require', which will require a
module dynamically and pull out a value.  In this case, you can just
use:

  (dynamic-require 'plot 'plot)

instead of `plot', and as long as that's not executed, the library
will not be used.  With a little more effort it's not too difficult to
write an `autoload' macro.  (But it is, of course, limited to
functions.)

-- 
          ((lambda (x) (x x)) (lambda (x) (x x)))          Eli Barzilay:
                    http://barzilay.org/                   Maze is Life!


Posted on the users mailing list.