When you load a Haskell source module into GHCi, it is
normally converted to byte-code and run using the interpreter.
However, interpreted code can also run alongside compiled code in
GHCi; indeed, normally when GHCi starts, it loads up a compiled
copy of the base
package, which contains the
Prelude
.
Why should we want to run compiled code? Well, compiled code is roughly 10x faster than interpreted code, but takes about 2x longer to produce (perhaps longer if optimisation is on). So it pays to compile the parts of a program that aren't changing very often, and use the interpreter for the code being actively developed.
When loading up source files with :load
,
GHCi looks for any corresponding compiled object files, and will
use one in preference to interpreting the source if possible. For
example, suppose we have a 4-module program consisting of modules
A, B, C, and D. Modules B and C both import D only,
and A imports both B & C:
A / \ B C \ / D
We can compile D, then load the whole program, like this:
Prelude> :! ghc -c D.hs Prelude> :load A Skipping D ( D.hs, D.o ) Compiling C ( C.hs, interpreted ) Compiling B ( B.hs, interpreted ) Compiling A ( A.hs, interpreted ) Ok, modules loaded: A, B, C, D. *Main>
In the messages from the compiler, we see that it skipped D,
and used the object file D.o
. The message
Skipping
module
indicates that compilation for module
isn't necessary, because the source and everything it depends on
is unchanged since the last compilation.
At any time you can use the command
:show modules
to get a list of the modules currently loaded
into GHCi:
*Main> :show modules D ( D.hs, D.o ) C ( C.hs, interpreted ) B ( B.hs, interpreted ) A ( A.hs, interpreted ) *Main>
If we now modify the source of D (or pretend to: using Unix
command touch
on the source file is handy for
this), the compiler will no longer be able to use the object file,
because it might be out of date:
*Main> :! touch D.hs *Main> :reload Compiling D ( D.hs, interpreted ) Skipping C ( C.hs, interpreted ) Skipping B ( B.hs, interpreted ) Skipping A ( A.hs, interpreted ) Ok, modules loaded: A, B, C, D. *Main>
Note that module D was compiled, but in this instance because its source hadn't really changed, its interface remained the same, and the recompilation checker determined that A, B and C didn't need to be recompiled.
So let's try compiling one of the other modules:
*Main> :! ghc -c C.hs *Main> :load A Compiling D ( D.hs, interpreted ) Compiling C ( C.hs, interpreted ) Compiling B ( B.hs, interpreted ) Compiling A ( A.hs, interpreted ) Ok, modules loaded: A, B, C, D.
We didn't get the compiled version of C! What happened? Well, in GHCi a compiled module may only depend on other compiled modules, and in this case C depends on D, which doesn't have an object file, so GHCi also rejected C's object file. Ok, so let's also compile D:
*Main> :! ghc -c D.hs *Main> :reload Ok, modules loaded: A, B, C, D.
Nothing happened! Here's another lesson: newly compiled
modules aren't picked up by :reload
, only
:load
:
*Main> :load A Skipping D ( D.hs, D.o ) Skipping C ( C.hs, C.o ) Compiling B ( B.hs, interpreted ) Compiling A ( A.hs, interpreted ) Ok, modules loaded: A, B, C, D.
HINT: since GHCi will only use a compiled object file if it
can be sure that the compiled version is up-to-date, a good technique
when working on a large program is to occasionally run
ghc ––make
to compile the whole project (say
before you go for lunch :-), then continue working in the
interpreter. As you modify code, the new modules will be
interpreted, but the rest of the project will remain
compiled.