moved no-cpontract section to Performance chapter, reacted to some Robby suggestions

This commit is contained in:
Matthias Felleisen 2019-05-18 12:56:48 -04:00
parent 52bde149f3
commit 11a25b3a54
2 changed files with 178 additions and 176 deletions

View File

@ -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}

View File

@ -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.