
Port `examples`, `interactions`, etc., to use the new `examples` form of `scribble/examples`. The main intended effect is to ensure that errors are produced by examples only as specifically indicated.
151 lines
5.7 KiB
Racket
151 lines
5.7 KiB
Racket
#lang scribble/doc
|
|
@(require "mz.rkt" scribble/struct racket/shared (for-label racket/shared))
|
|
|
|
|
|
@(define shared-eval (make-base-eval))
|
|
@examples[#:hidden #:eval shared-eval (require racket/shared)]
|
|
|
|
@(define maker
|
|
(make-element #f (list
|
|
(racketvarfont "prefix:")
|
|
(racketidfont "make-")
|
|
(racketvarfont "id"))))
|
|
@(define typedef
|
|
(make-element #f (list
|
|
(racketvarfont "prefix:")
|
|
(racketvarfont "id"))))
|
|
|
|
@title[#:tag "shared"]{Constructing Graphs: @racket[shared]}
|
|
|
|
@note-lib[racket/shared]
|
|
|
|
@defform[(shared ([id expr] ...) body ...+)]{
|
|
|
|
Binds @racket[id]s with shared structure according to @racket[exprs]
|
|
and then evaluates the @racket[body-expr]s, returning the result of
|
|
the last expression.
|
|
|
|
The @racket[shared] form is similar to @racket[letrec], except that
|
|
special forms of @racket[expr] are recognized (after partial macro
|
|
expansion) to construct graph-structured data, where the corresponding
|
|
@racket[letrec] would instead produce a use-before-initialization error.
|
|
|
|
Each @racket[expr] (after partial expansion) is matched against the
|
|
following @racket[_shared-expr] grammar, where earlier variants in a
|
|
production take precedence over later variants:
|
|
|
|
@racketgrammar*[
|
|
#:literals (cons list list* append vector-immutable box-immutable mcons vector box)
|
|
[shared-expr shell-expr
|
|
plain-expr]
|
|
[shell-expr (cons in-immutable-expr in-immutable-expr)
|
|
(list in-immutable-expr ...)
|
|
(list* in-immutable-expr ...)
|
|
(append early-expr ... in-immutable-expr)
|
|
(vector-immutable in-immutable-expr ...)
|
|
(box-immutable in-immutable-expr)
|
|
(mcons patchable-expr patchable-expr)
|
|
(vector patchable-expr ...)
|
|
(box patchable-expr)
|
|
(@#,|maker| patchable-expr ...)]
|
|
[in-immutable-expr shell-id
|
|
shell-expr
|
|
early-expr]
|
|
[shell-id id]
|
|
[patchable-expr expr]
|
|
[early-expr expr]
|
|
[plain-expr expr]
|
|
]
|
|
|
|
The @|maker| identifier above matches three kinds of references. The
|
|
first kind is any binding whose name has @racketidfont{make-} in the
|
|
middle, and where @|typedef| has a @tech{transformer} binding to
|
|
structure information with a full set of mutator bindings; see
|
|
@secref["structinfo"]. The second kind is an identifier that itself has a
|
|
@tech{transformer} binding to structure information. The third kind is an
|
|
identifier that has a @racket['constructor-for] @tech{syntax property}
|
|
whose value is an identifier with a @tech{transformer} binding to structure
|
|
information. A @racket[_shell-id], meanwhile, must be one of the
|
|
@racket[id]s bound by the @racket[shared] form to a
|
|
@racket[_shell-expr].
|
|
|
|
When the @racket[expr]s of the @racket[shared] form are parsed as
|
|
@racket[_shared-expr] (taking into account the order of the variants
|
|
for parsing precedence), the sub-expressions that were parsed via
|
|
@racket[_early-expr] will be evaluated first when the @racket[shared]
|
|
form is evaluated. Among such expressions, they are evaluated in the
|
|
order as they appear within the @racket[shared] form. However, any
|
|
reference to an @racket[id] bound by @racket[shared] produces
|
|
a use-before-initialization errror, even if the binding for the @racket[id] appears
|
|
before the corresponding @racket[_early-expr] within the
|
|
@racket[shared] form.
|
|
|
|
The @racket[_shell-id]s and @racket[_shell-expr]s (not counting
|
|
@racket[_patchable-expr] and @racket[_early-expr] sub-expressions) are
|
|
effectively evaluated next:
|
|
|
|
@itemlist[
|
|
|
|
@item{A @racket[_shell-id] reference produces the same value as the
|
|
corresponding @racket[_id] will produce within the
|
|
@racket[body]s, assuming that @racket[_id] is never mutated
|
|
with @racket[set!]. This special handling of a
|
|
@racket[_shell-id] reference is one way in which
|
|
@racket[shared] supports the creation of cyclic data, including
|
|
immutable cyclic data.}
|
|
|
|
@item{A @racket[_shell-expr] of the form @racket[(mcons
|
|
_patchable-expr _patchable-expr)], @racket[(vector _patchable-expr
|
|
...)], @racket[(box _patchable-expr)], or @racket[(@#,|maker|
|
|
_patchable-expr ...)] produces a mutable value whose content
|
|
positions are initialized to @racket[undefined]. Each content
|
|
position is @deftech{patched} (i.e., updated) after the
|
|
corresponding @racket[_patchable-expr] expression is later
|
|
evaluated.}
|
|
|
|
]
|
|
|
|
Next, the @racket[_plain-expr]s are evaluated as for @racket[letrec],
|
|
where a reference to an @racket[id] raises @racket[exn:fail:contract:variable] if it
|
|
is evaluated before the right-hand side of the @racket[id] binding.
|
|
|
|
Finally, the @racket[_patchable-expr]s are evaluated and their values
|
|
replace @racket[undefined]s in the results of
|
|
@racket[_shell-expr]s. At this point, all @racket[id]s are bound, so
|
|
@racket[_patchable-expr]s can create data cycles (but only with cycles
|
|
that can be created via mutation).
|
|
|
|
@examples[
|
|
#:eval shared-eval
|
|
(shared ([a (cons 1 a)])
|
|
a)
|
|
(shared ([a (cons 1 b)]
|
|
[b (cons 2 a)])
|
|
a)
|
|
(shared ([a (cons 1 b)]
|
|
[b 7])
|
|
a)
|
|
(eval:error (shared ([a a]) (code:comment @#,t{no indirection...})
|
|
a))
|
|
(eval:error (shared ([a (cons 1 b)] (code:comment @#,t{@racket[b] is early...})
|
|
[b a])
|
|
a))
|
|
(shared ([a (mcons 1 b)] (code:comment @#,t{@racket[b] is patchable...})
|
|
[b a])
|
|
a)
|
|
(shared ([a (vector b b b)]
|
|
[b (box 1)])
|
|
(set-box! b 5)
|
|
a)
|
|
(shared ([a (box b)]
|
|
[b (vector (unbox a) (code:comment @#,t{@racket[unbox] after @racket[a] is patched})
|
|
(unbox c))] (code:comment @#,t{@racket[unbox] before @racket[c] is patched})
|
|
[c (box b)])
|
|
b)
|
|
]}
|
|
|
|
|
|
@; ----------------------------------------------------------------------
|
|
|
|
@close-eval[shared-eval]
|