► |  | The Racket Guide

1 | Welcome to Racket
2 | Racket Essentials
3 | Built-In Datatypes
4 | Expressions and Definitions
5 | Programmer-Defined Datatypes
6 | Modules
7 | Contracts
8 | Input and Output
9 | Regular Expressions
10 | Exceptions and Control
11 | Iterations and Comprehensions
12 | Pattern Matching
13 | Classes and Objects
14 | Units
15 | Reflection and Dynamic Evaluation
16 | Macros
17 | Creating Languages
18 | Concurrency and Synchronization
19 | Performance
20 | Parallelism
21 | Running and Creating Executables
22 | More Libraries
23 | Dialects of Racket and Scheme
24 | Command-Line Tools and Your Editor of Choice
| Bibliography
| Index

▼ | 6 | Modules

6.1 | Module Basics
6.2 | Module Syntax
6.3 | Module Paths
6.4 | Imports: require
6.5 | Exports: provide
6.6 | Assignment and Redefinition
6.7 | Modules and Macros

top ← prev up next →

#### 6.6`` Assignment and Redefinition

The use of set! 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.

Examples:

> > (module m racket
> (provide counter increment!)
> (define counter 0)
> (define (increment!)
> (set! counter (add1 counter))))
> > (require 'm)
> > counter
> 0
> > (increment!)
> > counter
> 1
> > (set! counter -1)
> set!: cannot mutate module-required identifier
> at: counter
> in: (set! counter -1)

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,

> (module m racket
> (provide rx:fish fishy-string?)
> (define rx:fish #rx"fish")
> (define (fishy-string? s)
> (regexp-match? rx:fish 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 contains no set! of a particular identifier that is defined within the module, then the identifier is considered a constant that cannot be changed—not even by re-declaring the module.

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 use Racket’s reflection support to re-declare a module, 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.

> > (module m racket
> (define pie 3.141597))
> > (require 'm)
> > (module m racket
> (define pie 3))
> define-values: assignment disallowed;
> cannot re-define a constant
> constant: pie
> in module: 'm

For exploration and debugging purposes, the Racket reflective layer provides a compile-enforce-module-constants parameter to disable the enforcement of constants.

> > (compile-enforce-module-constants #f)
> > (module m2 racket
> (provide pie)
> (define pie 3.141597))
> > (require 'm2)
> > (module m2 racket
> (provide pie)
> (define pie 3))
> > (compile-enforce-module-constants #t)
> > pie
> 3

top ← prev up next →