moved no-cpontract section to Performance chapter, reacted to some Robby suggestions
This commit is contained in:
parent
52bde149f3
commit
11a25b3a54
|
@ -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}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue
Block a user