doc corrections and improvements related to submodules

In particular, add `module+' to the Guide.
This commit is contained in:
Matthew Flatt 2012-03-22 14:46:05 -06:00
parent d4d5ca70fb
commit 876bc6f02b
5 changed files with 191 additions and 59 deletions

View File

@ -7,38 +7,37 @@
(define submodule 'test)
(define run-anyways? #f)
(define do-test
(match-lambda
[(? string? s)
(do-test (string->path s))]
[(? path? p)
(define ps (path->string p))
(cond
(define (do-test e [check-suffix? #f])
(match e
[(? string? s)
(do-test (string->path s))]
[(? path? p)
(cond
[(directory-exists? p)
(for-each
(λ (dp)
(do-test (build-path p dp)))
(do-test (build-path p dp) #t))
(directory-list p))]
[(and (file-exists? p)
(regexp-match #rx"\\.rkt$" ps))
(define fmod `(file ,ps))
(define mod `(submod ,fmod ,submodule))
(or (not check-suffix?)
(regexp-match #rx#"\\.rkt$" (path->bytes p))))
(define mod `(submod ,p ,submodule))
(cond
[(module-declared? mod #t)
(dynamic-require mod #f)]
[(and run-anyways? (module-declared? fmod #t))
(dynamic-require fmod #f)])]
[(and run-anyways? (module-declared? p #t))
(dynamic-require p #f)])]
[(not (file-exists? p))
(error 'test "Given path ~e does not exist" p)])]))
(command-line
#:program (short-program+command-name)
#:once-each
[("--submodule" "-s") submodule-str
"Determines which submodule to load"
(set! submodule (string->symbol submodule-str))]
[("--submodule" "-s") name
"Runs submodule <name> (defaults to `test')"
(set! submodule (string->symbol name))]
[("--run-if-absent" "-r")
"When set, raco test will require the default module if the given submodule is not present."
"Require base module if submodule is absent"
(set! run-anyways? #t)]
#:args files+directories
(for-each do-test files+directories))
#:args file-or-directory
(for-each do-test file-or-directory))

View File

@ -1,5 +1,6 @@
#lang scribble/doc
@(require scribble/manual scribble/eval "guide-utils.rkt")
@(require scribble/manual scribble/eval "guide-utils.rkt"
(for-label rackunit))
@(define cake-eval (make-base-eval))
@ -153,9 +154,21 @@ independently. Furthermore, if @filepath{park.rkt} is compiled to a
bytecode file (via @exec{raco make}), then the code for
@filepath{park.rkt} or the code for @racket[zoo] can be loaded independently.
A @racket[module*] form is similar to a nested @racket[module] form,
but @racket[module*] inverts the possibilities for reference between
the submodule and enclosing module:
Submodules can be nested within submodules, and a submodule can be
referenced directly by a module other than its enclosing module by
using a @racket[submod] path as described in
@seclink["module-paths"]{a later section}.
A @racket[module*] form is similar to a nested @racket[module] form:
@specform[
(module* name-id initial-module-path-or-#f
decl ...)
]
The @racket[module*] form differs from @racket[module] in that it
inverts the possibilities for reference between the submodule and
enclosing module:
@itemlist[
@ -166,23 +179,56 @@ the submodule and enclosing module:
@item{A submodule declared with @racket[module*] can @racket[require]
its enclosing module, but the enclosing module cannot
@racket[require] the submodule. In addition, a @racket[module*]
form can specify @racket[#f] as its
@racket[_initial-module-path], in which case the submodule sees
all of the enclosing module's bindings---including bindings
that are not exported via @racket[provide].}
@racket[require] the submodule.}
]
As an example of @racket[module*], the following variant of
@filepath{cake.rkt} includes a @racket[main] submodule that calls
@racket[print-cake]:
In addition, a @racket[module*] form can specify @racket[#f] in place of an
@racket[_initial-module-path], in which case the submodule sees all of
the enclosing module's bindings---including bindings that are not
exported via @racket[provide].
One use of submodule declared with @racket[module*] and @racket[#f] is
to export additional bindings through a submodule that are not
normally exported from the module:
@racketmod[
#:file "cake.rkt"
racket
(provide print-cake)
(provide print-cacke)
(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))
(module* extras #f
(provide show))
]
In this revised @filepath{cake.rkt} module, @racket[show] is not
imported by a module that uses @racket[(require "cake.rkt")], since
most clients of @filepath{cake.rkt} will not want the extra function. A
module can require the @racket[extra] @tech{submodule}
(using the @racket[submod] form described in
@seclink["module-paths"]{a later section}) to access the otherwise
hidden @racket[show] function.
@; ----------------------------------------------------------------------
@section[#:tag "main-and-test"]{Main and Test Submodules}
The following variant of @filepath{cake.rkt} includes a @racket[main]
submodule that calls @racket[print-cake]:
@racketmod[
#:file "cake.rkt"
racket
(define (print-cake n)
(show " ~a " n #\.)
@ -198,31 +244,100 @@ racket
(print-cake 10))
]
Running a module does not run its @racket[module*]-defined submodules,
since the enclosing module cannot directly reference
@racket[module*]-defined submodules. Nevertheless, running the above
module via @exec{racket} or DrRacket prints a cake with 10 candles,
because the @racket[main] submodule} is a special case.
Running a module does not run its @racket[module*]-defined
submodules. Nevertheless, running the above module via @exec{racket}
or DrRacket prints a cake with 10 candles, because the @racket[main]
@tech{submodule} is a special case.
When a module is provided as a program name to the @exec{racket}
executable or run directly within DrRacket, if the module has a
@as-index{@racket[main] submodule}, the @racket[main] submodule is run after its
enclosing module. Declaring a @racket[main] submodule is often a
useful describe tests or other extra actions to be performed when a
module is run directly instead of @racket[required] as a library
within a larger program.
@as-index{@racket[main] submodule}, the @racket[main] submodule is run
after its enclosing module. Declaring a @racket[main] submodule
thus specifies extra actions to be performed when a module is run directly,
instead of @racket[required] as a library within a larger program.
A @racket[main] submodule does not have to be declared with
@racket[module*]. If the @racket[main] module does not need to use
bindings from its enclosing module, it can be declared with
@racket[module]. A @racket[main] submodule typically uses the
bindings of its enclosing module, however, so @racket[main] is usually
declared with @racket[module*].
@racket[module]. More commonly, @racket[main] is declared using a
third submodule form, @racket[module+]:
Submodules can be nested within submodules, and a submodule can be
referenced directly by a module other than its enclosing module by
using a @racket[submod] path as described in the
@seclink["module-paths"]{next section}.
@specform[
(module+ name-id
decl ...)
]
A submodule declared with @racket[module+] is like one declared with
@racket[module*] using @racket[#f] as its
@racket[_initial-module-path] (i.e., there's no
@racket[_initial-module-path] for @racket[module+]). In addition,
multiple @racket[module+] forms can specify the same submodule name,
in which case the bodies of the @racket[module+] forms are combined to
form a single submodule.
The splicing behavior of @racket[module+] is particularly useful for
defining a @racket[test] submodule, which can be conveniently run
using @exec{raco test} in much the same way that @racket[main] is
conveniently run with @exec{racket}. For example, the following
@filepath{physics.rkt} module exports @racket[drop] and
@racket[to-energy] functions, and it defines a @racket[test] module to
hold unit tests:
@racketmod[
#:file "physics.rkt"
racket
(module+ test
(require rackunit)
(define ε 1e-10))
(provide drop
to-energy)
(define (drop t)
(* 1/2 9.8 t t))
(module+ test
(check-= (drop 0) 0 ε)
(check-= (drop 10) 490 ε))
(define (to-energy m)
(* m (expt 299792458.0 2)))
(module+ test
(check-= (to-energy 0) 0 ε)
(check-= (to-energy 1) 9e+16 1e+15))
]
This module is equivalent to using @racket[module*]:
@racketmod[
#:file "physics.rkt"
racket
(provide drop
to-energy)
(define (drop t)
(* 1/2 #e9.8 t t))
(define (to-energy m)
(* m (expt 299792458 2)))
(module* test #f
(require rackunit)
(define ε 1e-10)
(check-= (drop 0) 0 ε)
(check-= (drop 10) 490 ε)
(check-= (to-energy 0) 0 ε)
(check-= (to-energy 1) 9e+16 1e+15))
]
Using @racket[module+] instead of @racket[module*] allows tests to be
interleaved with function definitions.
The splicing behavior of @racket[module+] is also sometimes helpful
for a @racket[main] module. In any case, @racket[(module+ main ....)]
is preferred as more readable than @racket[(module* main #f ....)].
@; ----------------------------------------------------------------------

View File

@ -7,15 +7,19 @@
@title[#:tag "test"]{@exec{raco test}: Run tests}
The @exec{raco test} command requires and runs the @racket['test]
submodules associated with paths given on the command line. When a
The @exec{raco test} command requires and runs the @racket[test]
submodule (if any) associated with each path given on the command line. When a
path refers to a directory, the tool recursively discovers all
internal files that end in @filepath{.rkt} and inspects them as well.
files that end in @filepath{.rkt} within the directory and runs their
@racket[test] submodules.
The @exec{raco test} command accepts a few flags:
@itemize[
@item{@DFlag{s} @nonterm{id} or @DFlag{submodule} @nonterm{id}--- Requires the submodule @nonterm{id} rather than @racket['test].}
@item{@Flag{s} @nonterm{name} or @DFlag{submodule} @nonterm{name}
--- Requires the submodule @nonterm{name} rather than @racket[test].}
@item{@DFlag{r} or @DFlag{run-if-absent}--- Requires the default module if the given submodule is not present in a file.}
@item{@Flag{r} or @DFlag{run-if-absent}
--- Requires the top-level module of a file if the relevant submodule is not
present.}
]

View File

@ -24,11 +24,16 @@ to another module.
Returns @racket[#f] if @racket[v] is a @tech{resolved module path},
@racket[#f] otherwise.}
@defproc[(make-resolved-module-path [path (or/c symbol? (and/c path? complete-path?))])
@defproc[(make-resolved-module-path [path (or/c symbol?
(and/c path? complete-path?)
(cons/c (or/c symbol?
(and/c path? complete-path?))
(listof symbol?)))])
resolved-module-path?]{
Returns a @tech{resolved module path} that encapsulates @racket[path].
If @racket[path] is not a symbol, it normally should be
Returns a @tech{resolved module path} that encapsulates @racket[path],
where a list @racket[path] corresponds to a @tech{submodule} path.
If @racket[path] is a path or starts with a path, the path normally should be
@tech{cleanse}d (see @racket[cleanse-path]) and simplified (see
@racket[simplify-path]).
@ -38,9 +43,14 @@ A @tech{resolved module path} is interned. That is, if two
@racket[eq?].}
@defproc[(resolved-module-path-name [module-path resolved-module-path?])
(or/c path? symbol?)]{
(or/c symbol?
(and/c path? complete-path?)
(cons/c (or/c symbol?
(and/c path? complete-path?))
(listof symbol?)))]{
Returns the path or symbol encapsulated by a @tech{resolved module path}.}
Returns the path or symbol encapsulated by a @tech{resolved module path}.
A list result corresponds to a @tech{submodule} path.}
@defproc[(module-path? [v any/c]) boolean?]{

View File

@ -290,6 +290,8 @@ See also @secref["module-eval-model"] and @secref["mod-parse"].
@defform*[((module* id module-path form ...)
(module* id #f form ...))]{
@guideintro["submodules"]{@racket[module*]}
Like @racket[module], but only for declaring a @tech{submodule} within
a module, and for submodules that may @racket[require] the enclosing module.
@ -306,6 +308,8 @@ have no effect on the submodule.}
@defform[(module+ id form ...)]{
@guideintro["main-and-test"]{@racket[module+]}
Declares and/or adds to a @tech{submodule} named @racket[id].
Each addition for @racket[id] is combined in order to form the entire