
Although "macOS" is the correct name for Apple's current desktop OS, we've decided to go with "Mac OS" to cover all of Apple's Unix-like desktop OS versions. The label "Mac OS" is more readable, clear in context (i.e., unlikely to be confused with the Mac OSes that proceeded Mac OS X), and as likely to match Apple's future OS names as anything.
310 lines
12 KiB
Racket
310 lines
12 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 command 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}
|
|
|
|
@margin-note{See @secref[#:doc '(lib "scribblings/raco/raco.scrbl")
|
|
"make"] for more information on @exec{raco make}.}
|
|
|
|
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.
|
|
|
|
@; ----------------------------------------
|
|
@section{Library Collections}
|
|
|
|
A @deftech{collection} is a hierarchical grouping 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] resolves the path by searching among
|
|
installed @tech{collections}, instead of treating the path as relative to
|
|
the enclosing module's path.}
|
|
|
|
]
|
|
|
|
To a first approximation, a @tech{collection} is implemented as a
|
|
filesystem directory. For example, the @filepath{racket} collection is
|
|
mostly located in a @filepath{racket} directory within the Racket
|
|
installation's @filepath{collects} directory, as reported by
|
|
|
|
@racketmod[
|
|
racket
|
|
|
|
(require setup/dirs)
|
|
|
|
(build-path (find-collects-dir) (code:comment @#,t{main collection directory})
|
|
"racket")
|
|
]
|
|
|
|
The Racket installation's @filepath{collects} directory, however, is
|
|
only one place that @racket[require] looks for collection directories.
|
|
Other places include the user-specific directory reported by
|
|
@racket[(find-user-collects-dir)] and directories configured through
|
|
the @envvar{PLTCOLLECTS} search path. Finally, and most typically,
|
|
collections are found through installed @tech{packages}.
|
|
|
|
@; ----------------------------------------
|
|
@section[#:tag "packages-and-collections"]{Packages and Collections}
|
|
|
|
A @deftech{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 @racketmodname[racket/gui] library is
|
|
provided by the @filepath{gui} package, while
|
|
@racketmodname[parser-tools/lex] is provided by the
|
|
@filepath{parser-tools} library.@margin-note{More precisely,
|
|
@racketmodname[racket/gui] is provided by @filepath{gui-lib},
|
|
@racketmodname[parser-tools/lex] is provided by
|
|
@filepath{parser-tools-lib}, and the @filepath{gui} and
|
|
@filepath{parser-tools} packages extend @filepath{gui-lib} and
|
|
@filepath{parser-tools-lib} with documentation.}
|
|
|
|
Racket programs do not refer to @tech{packages} directly. Instead,
|
|
programs refer to libraries via @tech{collections}, and adding or
|
|
removing a @tech{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 @other-manual['(lib
|
|
"pkg/scribblings/pkg.scrbl")].
|
|
|
|
@; ----------------------------------------
|
|
@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, the best option is add a new
|
|
@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.
|
|
|
|
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.@margin-note*{If you set @envvar{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 @tech{package}.
|
|
|
|
Creating a package @emph{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 @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, either
|
|
|
|
@itemlist[
|
|
|
|
@item{Use the @exec{raco pkg} command-line tool:
|
|
|
|
@commandline{raco pkg install --link /usr/molly/bakery}
|
|
|
|
where the @DFlag{link} flag is not actually needed when the
|
|
provided path includes a directory separator.}
|
|
|
|
@item{Use DrRacket's @onscreen{Package Manager} item from the
|
|
@onscreen{File} menu. In the @onscreen{Do What I Mean} panel,
|
|
click @onscreen{Browse...}, choose the
|
|
@filepath{/usr/molly/bakery} directory, and click
|
|
@onscreen{Install}.}
|
|
|
|
]
|
|
|
|
Afterward, @racket[(require bakery/cake)] from any module will import
|
|
the @racket[print-cake] function from
|
|
@filepath{/usr/molly/bakery/cake.rkt}.
|
|
|
|
By default, the name of the directory that you install is used both as
|
|
the @tech{package} name and as the @tech{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 @other-manual['(lib
|
|
"pkg/scribblings/pkg.scrbl")] 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 @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, @exec{raco setup} 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}.
|