The first-class access to modules and module variables described in the previous subsection is very powerful and allows Guile users to build many tools to dynamically learn things about their Guile systems. However, as Scheme godparent Mathias Felleisen wrote in “On the Expressive Power of Programming Languages”, a more expressive language is necessarily harder to reason about. There are transformations that Guile’s compiler would like to make which can’t be done if every top-level definition is subject to mutation at any time.
Consider this module:
(define-module (boxes) #:export (make-box box-ref box-set! box-swap!)) (define (make-box x) (list x)) (define (box-ref box) (car box)) (define (box-set! box x) (set-car! box x)) (define (box-swap! box x) (let ((y (box-ref box))) (box-set! box x) y))
Ideally you’d like for the box-ref
in box-swap!
to be
inlined to car
. Guile’s compiler can do this, but only if it
knows that box-ref
’s definition is what it appears to be in the
text. However, in the general case it could be that a programmer could
reach into the (boxes)
module at any time and change the value of
box-ref
.
To allow Guile to reason about the values of top-levels from a module, a
module can be marked as declarative. This flag applies only to
the subset of top-level definitions that are themselves declarative:
those that are defined within the compilation unit, and not assigned
(set!
) or redefined within the compilation unit.
To explicitly mark a module as being declarative, pass the
#:declarative?
keyword argument when declaring a module:
(define-module (boxes) #:export (make-box box-ref box-set! box-swap!) #:declarative? #t)
By default, modules are compiled declaratively if the
user-modules-declarative?
parameter is true when the
module is compiled.
A boolean indicating whether definitions in modules created by
define-module
or implicitly as part of a compilation unit without
an explicit module can be treated as declarative.
Because it’s usually what you want, the default value of
user-modules-declarative?
is #t
.
In the vast majority of use cases, declarative modules are what you want. However, there are exceptions.
Consider the (boxes)
module above. Let’s say you want to be able
to go in and change the definition of box-set!
at run-time:
scheme@(guile-user)> (use-modules (boxes)) scheme@(guile-user)> ,module boxes scheme@(boxes)> (define (box-set! x y) (set-car! x (pk y)))
However, considering that (boxes)
is a declarative module, it
could be that box-swap!
inlined the call to box-set!
– so
it may be that you are surprised if you call (box-swap! x y)
and
you don’t see the new definition being used. (Note, however, that Guile
has no guarantees about what definitions its compiler will or will not
inline.)
If you want to allow the definition of box-set!
to be changed and
to have all of its uses updated, then probably the best option is to
edit the module and reload the whole thing:
scheme@(guile-user)> ,reload (boxes)
The advantage of the reloading approach is that you maintain the
optimizations that declarative modules enable, while also being able to
live-update the code. If the module keeps precious program state, those
definitions can be marked as define-once
to prevent reloads from
overwriting them. See Top Level Variable Definitions, for more on define-once
.
Incidentally, define-once
also prevents declarative-definition
optimizations, so if there’s a limited subset of redefinable bindings,
define-once
could be an interesting tool to mark those
definitions as works-in-progress for interactive program development.
To users, whether a module is declarative or not is mostly immaterial:
besides normal use via use-modules
, users can reference and
redefine public or private bindings programmatically or interactively.
The only difference is that changing a declarative definition may not
change all of its uses. If this use-case is important to you, and if
reloading whole modules is insufficient, then you can mark all
definitions in a module as non-declarative by adding
#:declarative? #f
to the module definition.
The default of whether modules are declarative or not can be controlled
via the (user-modules-declarative?)
parameter mentioned above,
but care should be taken to set this parameter when the modules are
compiled, e.g. via (eval-when (expand) (user-modules-declarative?
#f))
. See Eval-when.
Alternately you can prevent declarative-definition optimizations by
compiling at the -O1
optimization level instead of the default
-O2
, or via explicitly passing -Ono-letrectify
to the
guild compile
invocation. See Compiling Scheme Code, for more on
compiler options.
One final note. Currently, definitions from declarative modules can only be inlined within the module they are defined in, and within a compilation unit. This may change in the future to allow Guile to inline imported declarative definitions as well (cross-module inlining). To Guile, whether a definition is inlinable or not is a property of the definition, not its use. We hope to improve compiler tooling in the future to allow the user to identify definitions that are out of date when a declarative binding is redefined.