some more criticisms from Robby and clarifications
This commit is contained in:
parent
ceef969d48
commit
7a6d567daf
|
@ -144,14 +144,12 @@ expands to many nested definitions and expressions every time it is used.
|
|||
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:
|
||||
For such cases, we recommend organizing the module into a main module as
|
||||
usual and a submodule called @tt{no-contract} so that
|
||||
@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.}
|
||||
@item{the @tt{no-contract} submodule @racket[provide]s the functionality @emph{without} contracts,}
|
||||
@item{the main module @racket[provide]s the functionality @emph{with} contracts.}
|
||||
]
|
||||
This section explains three strategies for three different situations and
|
||||
levels of implementation complexity.
|
||||
|
@ -160,9 +158,9 @@ levels of implementation complexity.
|
|||
explains the basics of our understanding of ``safety'' and link to it.}
|
||||
@;
|
||||
@bold{Warning} Splitting contracted functionality into two modules in
|
||||
this way renders the code in the @tt{no-contract} @bold{unsafe}. The
|
||||
this way renders the code in the @tt{no-contract} module @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
|
||||
functions' 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,
|
||||
|
@ -216,7 +214,8 @@ the @racket[#:unprotected-submodule] functionality of @racket[contract-out].
|
|||
|
||||
(provide
|
||||
(contract-out
|
||||
(code:hilite #:unprotected-submodule) (code:hilite no-contract)
|
||||
(code:hilite #:unprotected-submodule)
|
||||
(code:hilite no-contract)
|
||||
(human strategy/c)
|
||||
(ai strategy/c)))
|
||||
|
||||
|
@ -233,8 +232,8 @@ the @racket[#:unprotected-submodule] functionality of @racket[contract-out].
|
|||
((general 'tra) x))))
|
||||
]
|
||||
|
||||
The example labeled @tt{good} illustrates what the module might look
|
||||
like originally. Every exported function comes with a contract, and the
|
||||
The module called @tt{good} illustrates what the code might look
|
||||
like originally. Every exported functions come with contracts, and the
|
||||
definitions of these functions can be found below the @racket[provide]
|
||||
specification in the module body. The @tt{fast} module on the right
|
||||
requests the creation of a submodule named @tt{no-contract}, which exports
|
||||
|
@ -250,12 +249,12 @@ straightforward:
|
|||
@tt{needs-goodness}
|
||||
racket
|
||||
|
||||
(require coll/fast)
|
||||
(require "fast.rkt")
|
||||
|
||||
human
|
||||
;; comes with contracts
|
||||
;; as if we had required
|
||||
;; coll/good
|
||||
;; "good.rkt" itself
|
||||
|
||||
(define state1 0)
|
||||
(define state2
|
||||
|
@ -269,7 +268,7 @@ straightforward:
|
|||
|
||||
(require
|
||||
(submod
|
||||
coll/fast
|
||||
"fast.rkt"
|
||||
no-contract))
|
||||
|
||||
human
|
||||
|
@ -281,16 +280,16 @@ straightforward:
|
|||
(define action*
|
||||
(map human state*))))
|
||||
]
|
||||
Both modules @racket[require] the @tt{fast} module, but the left one goes
|
||||
through the contracted @racket[provide] while and the right one uses the
|
||||
@tt{no-contract} submodule. 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.
|
||||
Both modules @racket[require] the @tt{fast} module, but @tt{needs-goodness}
|
||||
on the left goes through the contracted @racket[provide] while
|
||||
@tt{needs-speed} on the right uses the @tt{no-contract} submodule. Tchnically,
|
||||
the left module imports @racket[human] with contracts; the right one
|
||||
imports the same function without contract and thus doesn't have to pay the
|
||||
performance penalty.
|
||||
|
||||
Notice, however, that when you run these two client modules---assuming you
|
||||
have installed @tt{fast} in some collection @tt{coll} appropriately---the
|
||||
left one raises an contract error while the right one binds
|
||||
@racket[action*] to
|
||||
saved them with the correct names in some folder---the left one raises a
|
||||
contract error while the right one binds @racket[action*] to
|
||||
|
||||
@;%
|
||||
@(begin
|
||||
|
@ -301,7 +300,7 @@ left one raises an contract error while the right one binds
|
|||
@;%
|
||||
|
||||
The @tt{no-contract} submodule generated by this first, easy approach
|
||||
depends on @racketmodname[#, 'racket/contract] at both compile and run time.
|
||||
retains the dependency on @racketmodname[#, 'racket/contract] at both compile and run time.
|
||||
Here is a variant of the above module that demonstrates this point:
|
||||
@;%
|
||||
@(begin
|
||||
|
@ -335,10 +334,10 @@ contracts, requiring the @tt{no-contract} still raises a contract error:
|
|||
(require (submod "." server no-contract))
|
||||
))
|
||||
@;%
|
||||
@bold{Explanation} The @racket[require] runs the body of the main module,
|
||||
and doing so checks the first-order properties of the exported values---and
|
||||
because @racket[human] is not a function, this evaluation raises a contract
|
||||
error.
|
||||
@bold{Explanation} The @tt{no-contract} submodule depends on the main
|
||||
module, so the require runs the body of the main module, and doing so
|
||||
checks the first-order properties of the exported values. Because
|
||||
@racket[human] is not a function, this evaluation raises a contract error.
|
||||
|
||||
The @emph{second} way to create a @tt{no-contract} submodule requires
|
||||
systematic work from the developer and eliminates the run-time dependency
|
||||
|
@ -349,7 +348,7 @@ above, with the right one derived manually from the one on the left:
|
|||
@(begin
|
||||
#reader scribble/comment-reader
|
||||
(racketmod0 #:file
|
||||
@tt{good}
|
||||
@tt{good2}
|
||||
racket
|
||||
|
||||
(define state? zero?)
|
||||
|
@ -377,7 +376,7 @@ above, with the right one derived manually from the one on the left:
|
|||
@(begin
|
||||
#reader scribble/comment-reader
|
||||
(racketmod0 #:file
|
||||
@tt{fast}
|
||||
@tt{fast2}
|
||||
racket
|
||||
|
||||
(define state? zero?)
|
||||
|
@ -410,20 +409,25 @@ above, with the right one derived manually from the one on the left:
|
|||
(require 'no-contract)
|
||||
))
|
||||
]
|
||||
Here the @tt{fast} module on the right encapsulates the
|
||||
The @tt{fast2} 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{good} module
|
||||
this submodule exports the exact same identifiers as the @tt{good2} 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.
|
||||
|
||||
While this second way of creating a @tt{no-contract} submodule eliminates
|
||||
the run-time dependency on @racketmodname[#, 'racket/contract], its
|
||||
compilation---as a part of the outer module---still depends on this
|
||||
library, which is problematic in a few remaining situations.
|
||||
|
||||
The @emph{third} and last way to create a @tt{no-contract} submodule is
|
||||
useful when the presence of contracts prevents a module from being used in
|
||||
a context where contracts aren't available at all. One example is
|
||||
@rkt/base[]; another is the contracts library itself. Again, you may wish
|
||||
you had the same library without contracts. For these cases, we recommend a
|
||||
file-based strategy one. Assuming the library is located at @tt{a/b/c}, we
|
||||
recommend
|
||||
useful when contracts prevents a module from being used in a context where
|
||||
contracts aren't available at all---neither at compile nor at run time. One
|
||||
example is @rkt/base[]; another is the contracts library itself. Again, you
|
||||
may wish you had the same library without contracts. For these cases, we
|
||||
recommend a file-based strategy one. Assuming the library is located at
|
||||
@tt{a/b/c}, we recommend
|
||||
|
||||
@itemlist[#:style 'ordered
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user