Introduction

Guile Load Path is a place (or more precisely places), where Guile looks for the source code. This is the first thing one needs to set correctly to work on a Guile Scheme project. It makes Guile aware both of your own modules and external dependencies. For more details refer to Load Paths page in Guile Reference Manual. This guide will focus on getting and setting the right values for it and discussing different approaches to do so.

Getting The Load Paths

The current value of all load paths for the current guile process is stored in %load-path variable, so it's easy to obtain it in runtime using CLI, REPL or your IDE. To see what value it has you can just pretty print the variable.

> guile -c '((@ (ice-9 pretty-print) pretty-print) %load-path)'

("/home/bob/.guix-home/profile/share/guile/site/3.0"
 "/run/current-system/profile/share/guile/site/3.0"
 "/gnu/store/37m0a0ydy74wl2qrf2w1jdgqhxwbaxac-guile-3.0.9/share/guile/3.0"
 …)

By default with a clean Guile installation you should see only the directory which contains Guile's own source code. In more real-world scenarios, for example, in Guix System, I see two combined directories containing my system's and home's guile packages respectively, the directory with guile's sources and a few bogus entries, which I have truncated with ellipsis ().

> echo $GUILE_LOAD_PATH
/home/bob/.guix-home/profile/share/guile/site/3.0:/run/current-system/profile/share/guile/site/3.0

The first two directories comes from the environment variable $GUILE_LOAD_PATH. The rest are baked into guile distribution.

Getting a Clean Environment

To get a clean environment you can unset $GUILE_LOAD_PATH variable (to ensure there are no dependencies added to the project accidentally). You can also construct a pure environment with guix shell --pure guile, which has similiar effect, but for the sake of clarity we will unset the variable manually.

> unset GUILE_LOAD_PATH
> guile -c '((@ (ice-9 format) format) #t "~y" %load-path)'
("/gnu/store/37m0a0ydy74wl2qrf2w1jdgqhxwbaxac-guile-3.0.9/share/guile/3.0"
 …)

The guile's libraries directory is still present in load paths. It is hardcoded in the guile binary and not affected by GUILE_LOAD_PATH.

Setting GUILE_LOAD_PATH

Now, let's assume there is a project module (myproj core) in ./tmp/guile/myproj/core.scm' (usually it is src instead of tmp, but we provide examples, which are easy to clean up by invoking rm ./tmp -r).

> mkdir -p tmp/guile/myproj \
echo "(define-module (myproj core))" > tmp/guile/myproj/core.scm \
echo "(define-public (main) (display 'hi))" >> tmp/guile/myproj/core.scm

Trying to run guile -c '(begin (use-modules (myproj core)) (main))' leads to no code for module (myproj core) because this module is not on the load path yet. To fix this, you have to add tmp/guile to the load path. Pay attention that we add tmp/guile and not tmp or tmp/guile/proj. The module (myproj core) have to be in myproj/core.scm file, which must be located in the root of a load path, if this convention violated the module won't be found. Let's temporary set GUILE_LOAD_PATH for the next invocation of guile.

> GUILE_LOAD_PATH="./tmp/guile" \
guile -c '(begin (use-modules (myproj core)) (main))'
hi

Voila, we got a message printed by the main function.

The Order Matters

It's possible to have a few directories by separating them with colon.

> GUILE_LOAD_PATH="./test/guile:./tmp/guile" \
guile -c '((@ (ice-9 pretty-print) pretty-print) %load-path)'
("./test/guile"
 "./tmp/guile"
 "/gnu/store/37m0a0ydy74wl2qrf2w1jdgqhxwbaxac-guile-3.0.9/share/guile/3.0"
 …)

The order matters: if there are two modules with the same name on the load paths, the first one encountered will be used.

Also, there is a special three dots value (...) you can use when setting GUILE_LOAD_PATH. It will get expanded to the path of guile's own libraries. This is useful if you want to make sure your library doesn't override any built-ins. Keep in mind that this three dots is distinct from the ellipsis I have added when truncating the output of the guile command for this blog post.

> GUILE_LOAD_PATH="./tmp/guile:...:./my-srfis" \
guile -c '((@ (ice-9 pretty-print) pretty-print) %load-path)'
("./tmp/guile"
 "/gnu/store/37m0a0ydy74wl2qrf2w1jdgqhxwbaxac-guile-3.0.9/share/guile/3.0"
 …
 "./my-srfis")

Adding to GUILE_LOAD_PATH

Sometimes the GUILE_LOAD_PATH is already provided and set and you want to extend its value. It doesn't really matter if it comes from guix shell, set by the script of your colleague or whatever. For demonstration purposes, we will set it manually in our shell.

> export GUILE_LOAD_PATH="./path/set/by/someone-else"

Almost always, you want to prepend the directories you need to the existing value.

> GUILE_LOAD_PATH="./dev/guile:./test/guile:${GUILE_LOAD_PATH}" \
guile -c '((@ (ice-9 pretty-print) pretty-print) %load-path)'
("./dev/guile"
 "./test/guile"
 "./path/set/by/someone-else"
 "/gnu/store/37m0a0ydy74wl2qrf2w1jdgqhxwbaxac-guile-3.0.9/share/guile/3.0"
 …)

Pay attention that we set a variable only for the next invocation of guile. We do not mutate the variable value, and if we run this code a few times we won't see the duplicated values in the load paths.

To append to the existing GUILE_LOAD_PATH or order the directories anyhow else, just move ${GUILE_LOAD_PATH} accordingly.

In addition to GUILE_LOAD_PATH, there are at least two more ways to adjust load paths: via CLI and API. It's good to be aware of where else load path values can come from, especially when you work on someone else's project.

Modifying via CLI

For most cases GUILE_LOAD_PATH is enough and should be used, but sometimes it may be tricky or inconvenient to set environment variables. The guile command line program has the -L flag, which allows for prepending a directory to the load paths.

> guile -L ./dev/guile -L ./test/guile -c '((@ (ice-9 pretty-print) pretty-print) %load-path)'
("./dev/guile"
 "./test/guile"
 "./path/set/by/someone-else"
 "/gnu/store/37m0a0ydy74wl2qrf2w1jdgqhxwbaxac-guile-3.0.9/share/guile/3.0"
 …)

This is a complete equivalent to the example from the previous section. The order of added directories still matters.

Modifying via API

So far, we have used %load-path to get the value of load paths, but we can also use it to set load paths with set!.

We do not recommend setting load paths dynamically (in runtime) via API. All load paths should be known before the guile process is started and thus they should be set via GUILE_LOAD_PATH, but if you are really sure you want to, there are a couple of things to know.

%load-path must be modified before the expression is evaluated. That means if you need to set load paths in the same expression you want to use the new value it should be wrapped into (eval-when (expand)). See Eval-when for more details.

> guile -c '(begin (eval-when (expand) (set! %load-path (list "tmp/guile")))
(use-modules (myproj core)) (main))'

Don't forget to preserve the necessary values of the %load-path, otherwise things can break, such as goto definition, loading modules, etc.

(set! %load-path (cons "new/dir" %load-path))

There is a convenience wrapper macro, which does both of those things: add-to-load-path, but we hope you will never need any info from this section.

Clean Up

To wrap up all the things:

  • Do rm -r ./tmp to clean up all the tails.
  • Thank authors of this guide.

Acknowledgments

Edited by Joseph Turner.