racket/collects/scribblings/guide/module-basics.scrbl
2012-06-14 17:20:05 -04:00

281 lines
10 KiB
Racket

#lang scribble/doc
@(require scribble/manual
scribble/eval
"guide-utils.rkt"
"module-hier.rkt"
(for-label setup/dirs
setup/link
racket/date))
@title[#:tag "module-basics"]{Module Basics}
Each Racket module typically resides in its own file. For example,
suppose the file @filepath{cake.rkt} contains the following module:
@racketmod[
#:file "cake.rkt"
racket
(provide print-cake)
(code:comment @#,t{draws a cake with @racket[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 @filepath{cake.rkt} to use the
@racket[print-cake] function, since the @racket[provide] line in
@filepath{cake.rkt} explicitly exports the definition
@racket[print-cake]. The @racket[show] function is private to
@filepath{cake.rkt} (i.e., it cannot be used from other modules),
since @racket[show] is not exported.
The following @filepath{random-cake.rkt} module imports
@filepath{cake.rkt}:
@racketmod[
#:file "random-cake.rkt"
racket
(require "cake.rkt")
(print-cake (random 30))
]
The relative reference @racket["cake.rkt"] in the import
@racket[(require "cake.rkt")] works if the @filepath{cake.rkt} and
@filepath{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.
@; ----------------------------------------
@section[#:tag "module-org"]{Organizing Modules}
The @filepath{cake.rkt} and @filepath{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 @filepath{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 @filepath{db/lookup.rkt} that uses helper modules
@filepath{db/barcodes.rkt} and @filepath{db/makers.rkt}. Similarly,
the sorting-machine driver @filepath{machine/control.rkt} might use
helper modules @filepath{machine/sensors.rkt} and
@filepath{machine/actuators.rkt}.
@centerline[module-hierarchy]
The @filepath{sort.rkt} module uses the relative paths
@filepath{db/lookup.rkt} and @filepath{machine/control.rkt} to import
from the database and machine-control libraries:
@racketmod[
#:file "sort.rkt"
racket
(require "db/lookup.rkt" "machine/control.rkt")
....]
The @filepath{db/lookup.rkt} module similarly uses paths relative to
its own source to access the @filepath{db/barcodes.rkt} and
@filepath{db/makers.rkt} modules:
@racketmod[
#:file "db/lookup.rkt"
racket
(require "barcode.rkt" "makers.rkt")
....]
Ditto for @filepath{machine/control.rkt}:
@racketmod[
#:file "machine/control.rkt"
racket
(require "sensors.rkt" "actuators.rkt")
....]
Racket tools all work automatically with relative paths. For example,
@commandline{racket sort.rkt}
on the comamnd line runs the @filepath{sort.rkt} program and
automatically loads and compiles required modules. With a large enough
program, compilation from source can take too long, so use
@commandline{raco make sort.rkt}
to compile @filepath{sort.rkt} and all its dependencies to bytecode
files. Running @exec{racket sort.rkt} will automatically use bytecode
files when they are present.
@margin-note{See @secref[#:doc '(lib "scribblings/raco/raco.scrbl")
"make"] for more information on @exec{raco make}.}
@; ----------------------------------------
@section{Library Collections}
A @deftech{collection} is a set of installed library modules. A
module in a @tech{collection} is referenced through an unquoted,
suffixless path. For example, the following module refers to the
@filepath{date.rkt} library that is part of the @filepath{racket}
@tech{collection}:
@racketmod[
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 binding's documentation by clicking on hyperlinks, you can
hover over the binding name to find out which modules provide
it.
A module reference like @racketmodname[racket/date] looks like an
identifier, but it is not treated in the same way as @racket[printf]
or @racket[date->string]. Instead, when @racket[require] sees a module
reference that is unquoted, it converts the reference to a
collection-based module path:
@itemlist[
@item{First, if the unquoted path contains no @litchar{/}, then
@racket[require] automatically adds a @filepath{/main} to the
reference. For example, @racket[(require
@#,racketmodname[slideshow])] is equivalent to @racket[(require
slideshow/main)].}
@item{Second, @racket[require] implicitly adds a @filepath{.rkt}
suffix to the path.}
@item{Finally, @racket[require] treats the path as relative to the
installation location of the collection, instead of relative to
the enclosing module's path.}
]
The @filepath{racket} collection is located in a directory with the
Racket installation. A user-specific directory can contain additional
collections, and even more collection directories can be specified in
configuration files or through the @envvar{PLTCOLLECTS} search
path. Try running the following program to find out how your
collection search path is configured:
@racketmod[
racket
(require setup/dirs)
(find-collects-dir) (code:comment @#,t{main collection directory})
(find-user-collects-dir) (code:comment @#,t{user-specific collection directory})
(get-collects-search-dirs) (code:comment @#,t{complete search path})
]
@; ----------------------------------------
@section[#:tag "link-collection"]{Adding Collections}
Looking back at the candy-sorting example of @secref["module-org"],
suppose that modules in @filepath{db/} and @filepath{machine/} need a
common set of helper functions. Helper functions could be put in a
@filepath{utils/} directory, and modules in @filepath{db/} or
@filepath{machine/} could access utility modules with relative paths
that start @filepath{../utils/}. As long as a set of modules work
together in a single project, it's 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, you have two options:
@itemlist[
@item{Add the library to a new or existing @tech{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.}
@item{Add the library to a new or existing @|PLaneT| package. Libraries
in a @|PLaneT| package are referenced with a path of the form
@racket[(planet ....)] path.
@margin-note*{See @other-doc['(lib "planet/planet.scrbl")]
for more information on @|PLaneT|.}}
]
The simplest option is to add a new collection. You could add a new
collection by placing files in the Racket installation or one of the
directories reported by
@racket[(get-collects-search-dirs)]. Alternatively, you could add to
the list of searched directories by setting the @envvar{PLTCOLLECTS}
environment variable; if you set @envvar{PLTCOLLECTS}, include an
empty path in by starting the value with a colon (Unix and Mac OS X)
or semicolon (Windows) so that the original search paths are
preserved. Finally, instead of using one of the default directories or
setting @envvar{PLTCOLLECTS}, you can use @exec{raco link}.
The @exec{raco link} command-line tool creates a link from a
collection name to a directory for the collection's modules. For
example, suppose you have a directory @filepath{/usr/molly/bakery}
that contains the @filepath{cake.rkt} module (from the
@seclink["module-basics"]{beginning} of this section) and other
related modules. To make the modules available as a @filepath{bakery}
collection, use @margin-note*{Instead of installing a single
collection directory, the @DFlag{root} or @Flag{d} flag for @exec{raco
link} can install a directory that contains collections, much like
adding to @envvar{PLTCOLLECTS}.}
@commandline{raco link /usr/molly/bakery}
Afterward, @racket[(require bakery/cake)] from any module will import
the @racket[print-cake] function from
@filepath{/usr/molly/bakery/cake.rkt}.
To make a collection name different from the name of the directory
that contains the collection's modules, use the @DFlag{name} or
@Flag{n} option for @exec{raco link}. By default, @exec{raco link}
installs a collection link only for the current user, but you can
supply the @DFlag{installation} or @Flag{i} flag to install the link
for all users of your Racket installation.
@margin-note{See @secref[#:doc '(lib "scribblings/raco/raco.scrbl")
"link"] for more information on @exec{raco link}.}
If you intend to distribute your library collection to others, choose
the collection name carefully. The collection namespace is
hierarchical, but (unlike @|PLaneT|) the collection system has no
built-in feature to avoid conflicts from different producers or
different versions. Consider putting one-off libraries under some
top-level name like @filepath{molly} that identifies the producer.
Use a collection name like @filepath{bakery} when producing the
definitive collection of baked-goods libraries.
After your libraries are put in a @tech{collection} you can still
use @exec{raco make} to compile the library sources, but it's better
and more convenient to use @exec{raco setup}. The @exec{raco setup}
command takes a collection name (as opposed to a file name) and
compiles all libraries within the collection. In addition, it can
build documentation for the collection and add it to the documentation
index, as specified by a @filepath{info.rkt} module in the collection.
See @secref[#:doc '(lib "scribblings/raco/raco.scrbl") "setup"] for
more information on @exec{raco setup}.