8.6 Assignment and Redefinition
The use of := on variables defined within a module is limited to the body of the defining module. That is, a module is allowed to change the value of its own definitions, and such changes are visible to importing modules. However, an importing context is not allowed to change the value of an imported binding.
counter
increment
fun increment():
counter // prints 0
increment()
counter // prints 1
counter := -1 // not allowed
As the above example illustrates, a module can always grant others the ability to change its exports by providing a mutator function, such as increment.
The prohibition on assignment of imported variables helps support modular reasoning about programs. For example, in the module,
rx_fish
fishy_string
fun fishy_string(s):
the function fishy_string will always match strings that contain “fish”, no matter how other modules use the rx_fish binding. For essentially the same reason that it helps programmers, the prohibition on assignment to imports also allows many programs to be executed more efficiently.
Along the same lines, when a module’s binding is not declared
mutable, then the binding is considered a
constant that cannot be changed—
Consequently, re-declaration of a module is not generally allowed. For file-based modules, simply changing the file does not lead to a re-declaration in any case, because file-based modules are loaded on demand, and the previously loaded declarations satisfy future requests. It is possible to re-declare a module reflectively, however, and non-file modules can be re-declared in the REPL; in such cases, the re-declaration may fail if it involves the re-definition of a previously constant binding.