Add section on separate compilation to reference
This commit is contained in:
parent
dd0f0b6141
commit
aaa892646a
|
@ -598,6 +598,146 @@ top-level variables in higher @tech{phases}, while module
|
||||||
@tech{instantiations} (triggered by @racket[require]) relative to such
|
@tech{instantiations} (triggered by @racket[require]) relative to such
|
||||||
top-levels are in corresponding higher @tech{phase}s.
|
top-levels are in corresponding higher @tech{phase}s.
|
||||||
|
|
||||||
|
@;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
@subsection[#:tag "separate-compilation"]{The Separate Compilation Guarantee}
|
||||||
|
|
||||||
|
When a module is compiled, its @tech{phase} 1 is instantiated. This
|
||||||
|
can, in turn, trigger the transitive instantiation of many other
|
||||||
|
modules at other phases, including phase 1. Racket provides a very
|
||||||
|
strong guarantee about this instantiation called "The Separate
|
||||||
|
Compilation Guarantee":
|
||||||
|
|
||||||
|
"Any @tech{effects} of the instantiation of the module's phase 1 due
|
||||||
|
to compilation on the Racket runtime system are @tech{discarded}."
|
||||||
|
|
||||||
|
The guarantee concerns @deftech{effects}. There are two different
|
||||||
|
kinds of effects: internal and external.
|
||||||
|
|
||||||
|
Internal effects are exemplified by mutation. Mutation is the action
|
||||||
|
of a function such as @racket[set-box!], which changes the value
|
||||||
|
contained in the box. The modified box is not observable outside of
|
||||||
|
Racket, so the effect is said to be "internal". By definition,
|
||||||
|
internal effects is not detectable outside of the Racket program.
|
||||||
|
|
||||||
|
External effects are exemplified by input/output (or I/O). I/O is the
|
||||||
|
action of a function such as @racket[tcp-connect], which communicates
|
||||||
|
with the operating system to send network packets outside of the
|
||||||
|
machine running Racket via the electromagnetic spectrum. The
|
||||||
|
transmission of these packets is observable outside of Racket, in
|
||||||
|
particular by the receiver computer or any routers in
|
||||||
|
between. External effects exist to be detectable outside of the Racket
|
||||||
|
program and are often detectable using physical processes.
|
||||||
|
|
||||||
|
An effect is @deftech{discarded} when it is no longer detectable. For
|
||||||
|
instance, a mutation of a box from @racket[3] to @racket[4] would be
|
||||||
|
discarded if it ceases to be detectable that it was ever changed, and
|
||||||
|
thus would still contain @racket[3]. Because external effects are
|
||||||
|
intrinsically observable outside of Racket, they cannot be discarded,
|
||||||
|
because Racket lacks a time-reversal mechanism.
|
||||||
|
|
||||||
|
Thus, The Separate Compilation Guarantee only concerns effects like
|
||||||
|
mutation, because they are exclusively effects "on the Racket runtime
|
||||||
|
system" and not "on the physical universe".
|
||||||
|
|
||||||
|
There are many things a Racket program can do that appear to be
|
||||||
|
internal effects, but are actually external effects. For instance,
|
||||||
|
@racket[bytes-set!] is typically an internal effect, except when the
|
||||||
|
bytes were created by @racket[make-shared-bytes] which is allocated in
|
||||||
|
space observable by other processes. Thus, effects which modify them
|
||||||
|
are not discardable, so @racket[bytes-set!], in this case, is an
|
||||||
|
external effect.
|
||||||
|
|
||||||
|
The opposite is also true: some things which appear to be external are
|
||||||
|
actually internal. For instance, if a Racket program starts multiple
|
||||||
|
threads and uses mutation to communicate between them, that mutation
|
||||||
|
is purely internal, because Racket's threads are defined entirely
|
||||||
|
internally.
|
||||||
|
|
||||||
|
Furthermore, whenever a Racket program calls an @tech{unsafe}
|
||||||
|
function, the Racket runtime system makes no promises about its
|
||||||
|
effects. For instance, all foreign calls use
|
||||||
|
@racketmodname[ffi/unsafe], so all foreign calls are unsafe and their
|
||||||
|
effects cannot be discarded by Racket.
|
||||||
|
|
||||||
|
Finally, The Separate Compilation Guarantee only concerns
|
||||||
|
instantiations at phase 1 during compilation and not all phase 1
|
||||||
|
instantiations generally, such as when its phase 1 is required and
|
||||||
|
used for effects via reflective mechanisms.
|
||||||
|
|
||||||
|
The practical consequence of this guarantee is that because effects
|
||||||
|
are never visible, no module can detect whether a module it
|
||||||
|
@racket[require]s is already compiled. Thus, it can never change the
|
||||||
|
compilation of one module to have already compiled a different module.
|
||||||
|
In particular, if module A is shared by the phase 1 portion of modules
|
||||||
|
X and Y, then any internal effects while X is compiled are not visible
|
||||||
|
during the compilation of Y, regardless of whether X and Y are
|
||||||
|
compiled during the same Racket runtime system.
|
||||||
|
|
||||||
|
The following set of modules demonstrate this guarantee. First, we
|
||||||
|
define a module with the ability to observe effects via a
|
||||||
|
@racket[box]:
|
||||||
|
|
||||||
|
@racketblock[
|
||||||
|
(module box racket/base
|
||||||
|
(provide (all-defined-out))
|
||||||
|
(define b (box 0)))
|
||||||
|
]
|
||||||
|
|
||||||
|
Next, we define two syntax transformers that use and mutate this box:
|
||||||
|
|
||||||
|
@RACKETBLOCK[
|
||||||
|
(module transformers racket/base
|
||||||
|
(provide (all-defined-out))
|
||||||
|
(require (for-syntax racket/base
|
||||||
|
'box))
|
||||||
|
(define-syntax (sett stx)
|
||||||
|
(set-box! b 2)
|
||||||
|
(syntax (void)))
|
||||||
|
(define-syntax (gett stx)
|
||||||
|
(quasisyntax (unsyntax (unbox b)))))
|
||||||
|
]
|
||||||
|
|
||||||
|
Next, we define a module that uses these transformers:
|
||||||
|
|
||||||
|
@racketblock[
|
||||||
|
(module user racket/base
|
||||||
|
(provide (all-defined-out))
|
||||||
|
(require 'transformers)
|
||||||
|
(sett)
|
||||||
|
(define gott (gett)))
|
||||||
|
]
|
||||||
|
|
||||||
|
Finally, we define a second module that uses these transformers:
|
||||||
|
|
||||||
|
@racketblock[
|
||||||
|
(module test racket/base
|
||||||
|
(require 'box 'transformers 'user)
|
||||||
|
(displayln gott)
|
||||||
|
(displayln (gett))
|
||||||
|
|
||||||
|
(sett)
|
||||||
|
(displayln (gett))
|
||||||
|
|
||||||
|
(displayln (unbox b))
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
This module displays:
|
||||||
|
@itemize[
|
||||||
|
@item{@litchar["2"], because the module @racket['user] expanded to @racket[2].}
|
||||||
|
@item{@litchar["0"], because the effects of compiling @racket['user] were discarded.}
|
||||||
|
@item{@litchar["2"], because the effect of @racket[(sett)] inside @racket['test] is not discarded.}
|
||||||
|
@item{@litchar["0"], because the effects at phase 1 are irrelevant to the phase 0 use of @racket[b].}
|
||||||
|
]
|
||||||
|
|
||||||
|
Furthermore, this display will never change, regardless of which order
|
||||||
|
these modules are compiled in or whether they are compiled at the same
|
||||||
|
time or separately.
|
||||||
|
|
||||||
|
In contrast, if these modules were changed to store the value of
|
||||||
|
@racket[b] in a file on the filesystem, then the program would only
|
||||||
|
display @litchar["2"].
|
||||||
|
|
||||||
@;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
@;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
@subsection[#:tag "cross-phase persistent-modules"]{Cross-Phase Persistent Modules}
|
@subsection[#:tag "cross-phase persistent-modules"]{Cross-Phase Persistent Modules}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user