diff --git a/collects/scribblings/guide/module-basics.scrbl b/collects/scribblings/guide/module-basics.scrbl index c62a8ddf3c..e925b1a1fc 100644 --- a/collects/scribblings/guide/module-basics.scrbl +++ b/collects/scribblings/guide/module-basics.scrbl @@ -52,8 +52,8 @@ racket 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.) +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} diff --git a/collects/scribblings/guide/module-paths.scrbl b/collects/scribblings/guide/module-paths.scrbl index d528fd9a43..92e0d6fcbf 100644 --- a/collects/scribblings/guide/module-paths.scrbl +++ b/collects/scribblings/guide/module-paths.scrbl @@ -207,3 +207,47 @@ and it should @italic{not} be used when a plain, portable The automatic @filepath{.ss} and @filepath{.rkt} conversions apply as with other forms.} + +@; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@specsubform/subs[#:literals (submod) + (submod base element ...+) + ([base module-path + "."] + [element id + ".."])]{ + +Refers to a submodule of @racket[base]. The sequence of +@racket[element]s within @racket[submod] specify a path of submodule +names to reach the final submodule. + +@examples[ + (module zoo racket + (module monkey-house racket + (provide monkey) + (define monkey "Curious George"))) + (require (submod 'zoo monkey-house)) + monkey +] + +Using @racket["."] as @racket[base] within @racket[submod] stands for the +enclosing module. When a path of the form @racket[(#,(racket quote) +id)] refers to a submodule, it is equivalent to @racket[(submod "." +id)]. + +Using @racket[".."] as an @racket[element] cancels one submodule step, effectively +referring to the enclosing module. For example, @racket[(submod "." "..")] +refers to the enclosing module of the submodule in which the path +appears. + +@examples[ + (module zoo racket + (module monkey-house racket + (provide monkey) + (define monkey "Curious George")) + (module crocodile-house racket + (require (submod "." ".." monkey-house)) + (provide dinner) + (define dinner monkey))) + (require (submod 'zoo crocodile-house)) + dinner +]} diff --git a/collects/scribblings/guide/module-syntax.scrbl b/collects/scribblings/guide/module-syntax.scrbl index 82cc57d78c..49ae3ba8f6 100644 --- a/collects/scribblings/guide/module-syntax.scrbl +++ b/collects/scribblings/guide/module-syntax.scrbl @@ -26,13 +26,15 @@ The longhand form of a module declaration, which works in a where the @racket[_name-id] is a name for the module, @racket[_initial-module-path] is an initial import, and each @racket[_decl] is an import, export, definition, or expression. In -the case of a file, @racket[_name-id] must match the name of the -containing file, minus its directory path or file extension. +the case of a file, @racket[_name-id] normally matches the name of the +containing file, minus its directory path or file extension, but +@racket[_name-id] is ignored when the module is @racket[require]d +through its file's path. The @racket[_initial-module-path] is needed because even the @racket[require] form must be imported for further use in the module body. In other words, the @racket[_initial-module-path] import -bootstraps the syntax available in the body. The most commonly used +bootstraps the syntax that is available in the body. The most commonly used @racket[_initial-module-path] is @racketmodname[racket], which supplies most of the bindings described in this guide, including @racket[require], @racket[define], and @racket[provide]. Another commonly used @@ -120,6 +122,108 @@ Unless otherwise specified, a module that is documented as a @racketmodname[racket]. The documented language name can be used directly with @racket[module] or @racket[require], too. +@; ---------------------------------------------------------------------- +@section[#:tag "submodules"]{Submodules} + +A @racket[module] form can be nested within a module, in which case +the nested @racket[module] form declares a +@deftech{submodule}. Submodules can be referenced directly by the +enclosing module using a quoted name. The following example prints +@racket["Tony"] by importing @racket[tiger] from the @racket[zoo] +submodule: + +@racketmod[ + #:file "park.rkt" + racket + + (module zoo racket + (provide tiger) + (define tiger "Tony")) + + (require 'zoo) + + tiger +] + +Running a module does not necessarily run its submodules. In the above +example, running @filepath{park.rkt} runs its submodule @racket[zoo] +only because the @filepath{park.rkt} module @racket[require]s the +@racket[zoo] submodule. Otherwise, a module and each of its submodules can be run +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: + +@itemlist[ + + @item{A submodule declared with @racket[module] can be + @racket[require]d by its enclosing module, but the submodule + cannot @racket[require] the enclosing module or lexically + reference the enclosing module's bindings.} + + @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].} + +] + +As an example of @racket[module*], the following variant of +@filepath{cake.rkt} includes a @racket[main] submodule that calls +@racket[print-cake]: + +@racketmod[ +#:file "cake.rkt" +racket + +(provide print-cake) + +(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* main #f + (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. + +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. + +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*]. + +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}. + @; ---------------------------------------------------------------------- @close-eval[cake-eval] diff --git a/collects/syntax/scribblings/modcode.scrbl b/collects/syntax/scribblings/modcode.scrbl index 68692e4fb7..e47e656639 100644 --- a/collects/syntax/scribblings/modcode.scrbl +++ b/collects/syntax/scribblings/modcode.scrbl @@ -6,6 +6,7 @@ @defmodule[syntax/modcode] @defproc[(get-module-code [path path?] + [#:submodule-path submodule-path (listof symbol?) '()] [#:sub-path compiled-subdir0 (and/c path-string? relative-path?) "compiled"] [compiled-subdir (and/c path-string? relative-path?) compiled-subdir0] [#:compile compile-proc0 (any/c . -> . any) compile] @@ -24,7 +25,9 @@ any]{ Returns a compiled expression for the declaration of the module -specified by @racket[path]. +specified by @racket[path] and @racket[submodule-path], where +@racket[submodule-path] is empty for a root module or a list for a +submodule. The @racket[compiled-subdir] argument defaults to @racket["compiled"]; it specifies the sub-directory to search for a compiled version of the diff --git a/collects/syntax/scribblings/module-reader.scrbl b/collects/syntax/scribblings/module-reader.scrbl index a385c0c6ab..f8478da85a 100644 --- a/collects/syntax/scribblings/module-reader.scrbl +++ b/collects/syntax/scribblings/module-reader.scrbl @@ -334,7 +334,8 @@ reading of the module language.} @defproc[(make-meta-reader [self-sym symbol?] [path-desc-str string?] [#:read-spec read-spec (input-port? . -> . any/c) (lambda (in) ....)] - [module-path-parser (any/c . -> . (or/c module-path? #f))] + [module-path-parser (any/c . -> . (or/c module-path? #f + (vectorof module-path?)))] [convert-read (procedure? . -> . procedure?)] [convert-read-syntax (procedure? . -> . procedure?)] [convert-get-info (procedure? . -> . procedure?)]) @@ -367,6 +368,8 @@ description of the expected language form in the error message. The result of @racket[read-spec] is converted to a module path using @racket[module-path-parser]. If @racket[module-path-parser] produces +a vector of module paths, they are tried in order using +@racket[module-declared?]. If @racket[module-path-parser] produces @racket[#f], a reader exception is raised in the same way as when @racket[read-spec] produces a @racket[#f]. The @racketmodname[planet] languages supply a @racket[module-path-parser] that converts a byte