You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
typesetting/quad/qtest/mds/module-basics.md

267 lines
11 KiB
Markdown

5 years ago
# Module Basics
Each Racket module typically resides in its own file. For example,
suppose the file `"cake.rkt"` contains the following module:
`"cake.rkt"`
```racket
#lang racket
(provide print-cake)
; draws a cake with n candles
(define (print-cake n)
(show " ~a " n #\.)
(show " .-~a-. " n #\|)
(show " | ~a | " n #\space)
(show "---~a---" n #\-))
(define (show fmt n ch)
(printf fmt (make-string n ch))
(newline))
```
Then, other modules can import `"cake.rkt"` to use the `print-cake`
function, since the `provide` line in `"cake.rkt"` explicitly exports
the definition `print-cake`. The `show` function is private to
`"cake.rkt"` \(i.e., it cannot be used from other modules\), since
`show` is not exported.
The following `"random-cake.rkt"` module imports `"cake.rkt"`:
`"random-cake.rkt"`
```racket
#lang racket
(require "cake.rkt")
(print-cake (random 30))
```
The relative reference `"cake.rkt"` in the import `(require "cake.rkt")`
works if the `"cake.rkt"` and `"random-cake.rkt"` modules are in the
same directory. Unix-style relative paths are used for relative module
references on all platforms, much like relative URLs in HTML pages.
## 1. Organizing Modules
The `"cake.rkt"` and `"random-cake.rkt"` example demonstrates the most
common way to organize a program into modules: put all module files in a
single directory \(perhaps with subdirectories\), and then have the
modules reference each other through relative paths. A directory of
modules can act as a project, since it can be moved around on the
filesystem or copied to other machines, and relative paths preserve the
connections among modules.
As another example, if you are building a candy-sorting program, you
might have a main `"sort.rkt"` module that uses other modules to access
a candy database and a control sorting machine. If the candy-database
module itself is organized into sub-modules that handle barcode and
manufacturer information, then the database module could be
`"db/lookup.rkt"` that uses helper modules `"db/barcodes.rkt"` and
`"db/makers.rkt"`. Similarly, the sorting-machine driver
`"machine/control.rkt"` might use helper modules `"machine/sensors.rkt"`
and `"machine/actuators.rkt"`.
\#<pict>
The `"sort.rkt"` module uses the relative paths `"db/lookup.rkt"` and
`"machine/control.rkt"` to import from the database and machine-control
libraries:
`"sort.rkt"`
```racket
#lang racket
(require "db/lookup.rkt" "machine/control.rkt")
....
```
The `"db/lookup.rkt"` module similarly uses paths relative to its own
source to access the `"db/barcodes.rkt"` and `"db/makers.rkt"` modules:
`"db/lookup.rkt"`
```racket
#lang racket
(require "barcode.rkt" "makers.rkt")
....
```
Ditto for `"machine/control.rkt"`:
`"machine/control.rkt"`
```racket
#lang racket
(require "sensors.rkt" "actuators.rkt")
....
```
Racket tools all work automatically with relative paths. For example,
  `racket sort.rkt`
on the command line runs the `"sort.rkt"` program and automatically
loads and compiles required modules. With a large enough program,
compilation from source can take too long, so use
  `raco make sort.rkt`
> See \[missing\] for more information on `raco make`.
to compile `"sort.rkt"` and all its dependencies to bytecode files.
Running `racket sort.rkt` will automatically use bytecode files when
they are present.
## 2. Library Collections
A _collection_ is a hierarchical grouping of installed library modules.
A module in a collection is referenced through an unquoted, suffixless
path. For example, the following module refers to the `"date.rkt"`
library that is part of the `"racket"` collection:
```racket
#lang racket
(require racket/date)
(printf "Today is ~s\n"
(date->string (seconds->date (current-seconds))))
```
When you search the online Racket documentation, the search results
indicate the module that provides each binding. Alternatively, if you
reach a bindings documentation by clicking on hyperlinks, you can hover
over the binding name to find out which modules provide it.
A module reference like `racket/date` looks like an identifier, but it
is not treated in the same way as `printf` or `date->string`. Instead,
when `require` sees a module reference that is unquoted, it converts the
reference to a collection-based module path:
* First, if the unquoted path contains no `/`, then `require`
automatically adds a `"/main"` to the reference. For example,
`(require slideshow)` is equivalent to `(require slideshow/main)`.
* Second, `require` implicitly adds a `".rkt"` suffix to the path.
* Finally, `require` resolves the path by searching among installed
collections, instead of treating the path as relative to the enclosing
modules path.
To a first approximation, a collection is implemented as a filesystem
directory. For example, the `"racket"` collection is mostly located in a
`"racket"` directory within the Racket installations `"collects"`
directory, as reported by
```racket
#lang racket
(require setup/dirs)
(build-path (find-collects-dir) ; main collection directory
"racket")
```
The Racket installations `"collects"` directory, however, is only one
place that `require` looks for collection directories. Other places
include the user-specific directory reported by
`(find-user-collects-dir)` and directories configured through the
`PLTCOLLECTS` search path. Finally, and most typically, collections are
found through installed packages.
## 3. Packages and Collections
A _package_ is a set of libraries that are installed through the Racket
package manager \(or included as pre-installed in a Racket
distribution\). For example, the `racket/gui` library is provided by the
`"gui"` package, while `parser-tools/lex` is provided by the
`"parser-tools"` library.
> More precisely, `racket/gui` is provided by `"gui-lib"`,
> `parser-tools/lex` is provided by `"parser-tools-lib"`, and the `"gui"`
> and `"parser-tools"` packages extend `"gui-lib"` and
> `"parser-tools-lib"` with documentation.
Racket programs do not refer to packages directly. Instead, programs
refer to libraries via collections, and adding or removing a package
changes the set of collection-based libraries that are available. A
single package can supply libraries in multiple collections, and two
different packages can supply libraries in the same collection \(but not
the same libraries, and the package manager ensures that installed
packages do not conflict at that level\).
For more information about packages, see \[missing\].
## 4. Adding Collections
Looking back at the candy-sorting example of Organizing Modules, suppose
that modules in `"db/"` and `"machine/"` need a common set of helper
functions. Helper functions could be put in a `"utils/"` directory, and
modules in `"db/"` or `"machine/"` could access utility modules with
relative paths that start `"../utils/"`. As long as a set of modules
work together in a single project, its best to stick with relative
paths. A programmer can follow relative-path references without knowing
about your Racket configuration.
Some libraries are meant to be used across multiple projects, so that
keeping the library source in a directory with its uses does not make
sense. In that case, the best option is add a new collection. After the
library is in a collection, it can be referenced with an unquoted path,
just like libraries that are included with the Racket distribution.
You could add a new collection by placing files in the Racket
installation or one of the directories reported by
`(get-collects-search-dirs)`. Alternatively, you could add to the list
of searched directories by setting the `PLTCOLLECTS` environment
variable.If you set `PLTCOLLECTS`, include an empty path in by starting
the value with a colon \(Unix and Mac OS\) or semicolon \(Windows\) so
that the original search paths are preserved. The best option, however,
is to add a package.
Creating a package _does not_ mean that you have to register with a
package server or perform a bundling step that copies your source code
into an archive format. Creating a package can simply mean using the
package manager to make your libraries locally accessible as a
collection from their current source locations.
For example, suppose you have a directory `"/usr/molly/bakery"` that
contains the `"cake.rkt"` module \(from the beginning of this section\)
and other related modules. To make the modules available as a `"bakery"`
collection, either
* Use the `raco pkg` command-line tool:
  `raco pkg install --link /usr/molly/bakery`
where the `--link` flag is not actually needed when the provided path
includes a directory separator.
* Use DrRackets Package Manager item from the File menu. In the Do What
I Mean panel, click Browse..., choose the `"/usr/molly/bakery"`
directory, and click Install.
Afterward, `(require bakery/cake)` from any module will import the
`print-cake` function from `"/usr/molly/bakery/cake.rkt"`.
By default, the name of the directory that you install is used both as
the package name and as the collection that is provided by the package.
Also, the package manager normally defaults to installation only for the
current user, as opposed to all users of a Racket installation. See
\[missing\] for more information.
If you intend to distribute your libraries to others, choose collection
and package names carefully. The collection namespace is hierarchical,
but top-level collection names are global, and the package namespace is
flat. Consider putting one-off libraries under some top-level name like
`"molly"` that identifies the producer. Use a collection name like
`"bakery"` when producing the definitive collection of baked-goods
libraries.
After your libraries are put in a collection you can still use `raco
make` to compile the library sources, but its better and more
convenient to use `raco setup`. The `raco setup` command takes a
collection name \(as opposed to a file name\) and compiles all libraries
within the collection. In addition, `raco setup` can build documentation
for the collection and add it to the documentation index, as specified
by a `"info.rkt"` module in the collection. See \[missing\] for more
information on `raco setup`.