diff --git a/pkgs/racket-doc/scribblings/style/some-performance.scrbl b/pkgs/racket-doc/scribblings/style/some-performance.scrbl index 304ef2e937..c9a1a33fe5 100644 --- a/pkgs/racket-doc/scribblings/style/some-performance.scrbl +++ b/pkgs/racket-doc/scribblings/style/some-performance.scrbl @@ -138,6 +138,184 @@ searchable values and a function that encapsulates the body. Every expansion is a single function call. In contrast, the macro on the right expands to many nested definitions and expressions every time it is used. +@; ----------------------------------------------------------------------------- +@section{No Contracts} + +Adding contracts to a library is good. + +On some occasions, contracts impose a significant performance penalty. +For such cases, we recommend organizing the module into two parts: +@itemlist[ + +@item{a submodule named @tt{no-contract}, which defines the +functionality and exports some of it to the surrounding module} + +@item{a @racket[provide] specification with a @racket[contract-out] clause +in the outer module that re-exports the desired pieces of functionality.} + +] + +@margin-note*{We will soon supply a Reference section in the Evaluation Model chapter that +explains the basics of our understanding of ``safety'' and link to it.} +@; +@bold{Note} Splitting contracted functionality into two modules in this way +renders the code in the @tt{no-contract} @bold{unsafe}. The creator of the +original code might have assumed certain constraints on some function's +arguments, and the contracts checked these constraints. While the +documentation of the @tt{no-contract} submodule is likely to state these +constraints, it is left to the client to check them. If the client code +doesn't check the constraints and the arguments don't satisfy them, the +code in the @tt{no-contract} submodule may go wrong in various ways. + +@compare[ +@;% +@(begin +#reader scribble/comment-reader +(racketmod0 #:file + @tt{correct} + racket + + (define state? ...) + (define action? ...) + (define strategy/c + (-> state? action?)) + + (provide + (contract-out + ;; people's strategy + (human strategy/c) + + ;; tree traversal + (ai strategy/c))) + + (code:comment #, @1/2-line[]) + (code:comment #, @t{implementation}) + + (define (general p) ... ) + + (define human + (general create-gui)) + + (define ai + (general traversal)))) + +@(begin +#reader scribble/comment-reader +(racketmod0 #:file + @tt{fast} + racket + + (define state? ...) + (define action? ...) + (define strategy/c + (-> state? action?)) + + (provide + (contract-out + ;; people's strategy + (human strategy/c) + + ;; tree traversal + (ai strategy/c))) + + (code:comment #, @1/2-line[]) + (code:comment #, @t{implementation}) + + (module no-contract racket + (provide + human + ai) + + (define (general p) ... ) + + (define human + (general create-gui)) + + (define ai + (general traversal))) + + (require 'no-contract))) +] + +The example labeled @tt{correct} illustrates what the module might look +like originally. Every exported function comes with a contract, and the +definitions of these functions can be found below the @racket[provide] +specification in the module body. By comparison, the @tt{fast} module on +the right encapsulates the definitions in a submodule called +@tt{no-contract}; the @racket[provide] in this submodule exports the exact +same identifiers as the @tt{correct} module on the left. The main module +@racket[require]s the submodule immediately, making the identifiers +available in the outer scope so that the contracted @code{provide} can +re-export them. + +@compare[ +@;% +@(begin +#reader scribble/comment-reader +(racketmod0 #:file + @tt{needs-correctness} + racket + + (require coll/fast) + + human + ;; comes with contracts + ;; as if we had required + ;; coll/correct + + (define state1 ...) + (define state2 (human state1)))) + +@(begin +#reader scribble/comment-reader +(racketmod0 #:file + @tt{needs-speed} + racket + + (require + (submod + coll/fast no-contract)) + + human + ;; comes without contracts + + (define state* + (build-list ...)) + (define action* + (map human state*)))) +] + +Once the submodule exists, using the library with or without contracts is +straightforward. Both modules from above @racket[require] @tt{fast}, but +the left one requires just @tt{fast} and the right one the submodule called +@tt{no-contract}. Hence the left module imports, say, @racket[human] with +contracts; the right one imports the same function without contract and +thus doesn't have to pay the performance penalty. + +In some cases, the presence of contracts prevents a module from being used +in a context where contracts aren't available, say, for @rkt/base[] or the +contracts library itself. Again, you may wish you had the same library +without contracts. For these cases, we recommend a different strategy than +the submodule one. Assuming the library is located at @tt{a/b/c}, we +recommend +@itemlist[#:style 'ordered + +@item{creating a @tt{private/} sub-directory with the file @tt{a/b/private/c-no-ctc.rkt},} + +@item{placing the functionality into @tt{c-no-ctc.rkt},} + +@item{importing it into @tt{a/b/c.rkt}, and} + +@item{exporting it from there with contracts.} + +] + +Once this arrangement is set up, a client module in a special context +@rkt/base[] or for @racketmodname[#, 'racket/contract] can use @racket[(require +a/b/private/c-no-ctc)]. In a regular module, though, it would suffice +to write @racket[(require a/b/c)] and doing so would import contracted +identifiers. + @; ----------------------------------------------------------------------------- @section{Unsafe: Beware} diff --git a/pkgs/racket-doc/scribblings/style/unit.scrbl b/pkgs/racket-doc/scribblings/style/unit.scrbl index 2da93da30f..b9575f900d 100644 --- a/pkgs/racket-doc/scribblings/style/unit.scrbl +++ b/pkgs/racket-doc/scribblings/style/unit.scrbl @@ -528,179 +528,3 @@ on mutually recursive functions. It would thus be impossible to distribute the @racket[find-path] and @racket[find-path*] functions from the preceding code display into two distinct submodules. -@; ----------------------------------------------------------------------------- -@section{No Contracts} - -Adding contracts to a library is good. - -On some occasions, contracts impose a significant performance penalty. -For such cases, we recommend organizing the module into two parts: -@itemlist[ - -@item{a submodule named @tt{no-contract}, which defines the -functionality and exports some of it to the surrounding module} - -@item{a @racket[provide] specification with a @racket[contract-out] clause -in the outer module that re-exports the desired pieces of functionality.} - -] - -@margin-note*{We will soon supply a Racket documentation chapter that -explains the basics of our understanding of ``safety'' and link to it.} -@; -@bold{Note} Splitting contracted functionality into two modules in this way -render the code in the @tt{no-contract} @bold{unsafe}. The creator of the -original code might have assumed certain constraints on some function's -arguments, and the contracts checked these constraints. While the -documentation of the @tt{no-contract} submodule is likely to state these -constraints, it is left to the client to check them. If the client code -doesn't check the constraints and the arguments don't satisfy them, the -code in the @tt{no-contract} submodule may go wrong in various ways. - -@compare[ -@;% -@(begin -#reader scribble/comment-reader -(racketmod0 #:file - @tt{correct} - racket - - (define state? ...) - (define action? ...) - (define strategy/c - (-> state? action?)) - - (provide - (contract-out - ;; people's strategy - (human strategy/c) - - ;; tree traversal - (ai strategy/c))) - - (code:comment #, @1/2-line[]) - (code:comment #, @t{implementation}) - - (define (general p) ... ) - - (define human - (general create-gui)) - - (define ai - (general traversal)))) - -@(begin -#reader scribble/comment-reader -(racketmod0 #:file - @tt{fast} - - (define state? ...) - (define action? ...) - (define strategy/c - (-> state? action?)) - - (provide - (contract-out - ;; people's strategy - (human strategy/c) - - ;; tree traversal - (ai strategy/c))) - - (code:comment #, @1/2-line[]) - (code:comment #, @t{implementation}) - - (module no-contract racket - (provide - human - ai) - - (define (general p) ... ) - - (define human - (general create-gui)) - - (define ai - (general traversal))) - - (require 'no-contract))) -] - -The example labeled @tt{correct} illustrates what the module might look -like originally. Every exported function comes with a contract, and the -definitions of these functions can be found below the @racket[provide] -specification in the module body. By comparison, the @tt{fast} module on -the right encapsulates the definitions in a submodule called -@tt{no-contract}; the @racket[provide] in this submodule exports the exact -same identifiers as the @tt{correct} module on the left. The main module -@racket[require]s the submodule immediately, making the identifiers -available in the outer scope so that the contracted @code{provide} can -re-export them. - -@compare[ -@;% -@(begin -#reader scribble/comment-reader -(racketmod0 #:file - @tt{needs-correctness} - racket - - (require coll/fast) - - human - ;; comes with contracts - ;; as if we had required - ;; coll/correct - - (define state1 ...) - (define state2 (human state1)))) - -@(begin -#reader scribble/comment-reader -(racketmod0 #:file - @tt{needs-speed} - racket - - (require - (submod - coll/fast no-contract)) - - human - ;; comes without contracts - - (define state* - (build-list ...)) - (define action* - (map human state*)))) -] - -Once the submodule exists, using the library with or without contracts is -straightforward. Both modules from above @racket[require] @tt{fast}, but -the left one requires just @tt{fast} and the right one the submodule called -@tt{no-contract}. Hence the left module imports, say, @racket[human] with -contracts; the right one imports the same function without contract and -thus doesn't have to pay the performance penalty. - -In some cases, the presence of contracts prevents a module from being used -in a context where contracts aren't available, say, for @rkt/base[] or the -contracts library itself. Again, you may wish you had the same library -without contracts. For these cases, we recommend a different strategy than -the submodule one. Assuming the library is located at @tt{a/b/c}, we -recommend -@itemlist[#:style 'ordered - -@item{creating a @tt{private/} sub-directory with the file @tt{a/b/private/c-no-ctc.rkt},} - -@item{placing the functionality into @tt{c-no-ctc.rkt},} - -@item{importing it into @tt{a/b/c.rkt}, and} - -@item{exporting it from there with contracts.} - -] - -Once this arrangement is set up, a client module in a special context -@rkt/base[] or for @racketmodname[#, 'racket/contract] can use @racket[(require -a/b/private/c-no-ctc)]. In a regular module, though, it would suffice -to write @racket[(require a/b/c)] and doing so would import contracted -identifiers.