racket/collects/scribblings/reference/shared.scrbl
Eli Barzilay ac26fe7554 A ton of @scheme*' -> @racket*' and related updates.
Also, updates some of the mzlib files to point at `racket/*' libraries
rather than to `scheme/*' ones.
2011-06-25 04:08:47 -04:00

133 lines
5.0 KiB
Racket

#lang scribble/doc
@(require "mz.rkt" scribble/struct racket/shared (for-label racket/shared))
@(define shared-eval (make-base-eval))
@(interaction-eval #: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 @|undefined-const|s.
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
@|undefined-const|, even if the binding for the @racket[id] appears
before the corresponding @racket[_early-expr] within the
@racket[shared] form.
The @racket[_shell-ids] and @racket[_shell-exprs] (not counting
@racket[_patchable-expr] and @racket[_early-expr] sub-expressions) are
effectively evaluated next. 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.
Next, the @racket[_plain-expr]s are evaluated as for @racket[letrec],
where a reference to an @racket[id] produces @|undefined-const| if it
is evaluated before the right-hand side of the @racket[id] binding.
Finally, the @racket[_patchable-expr]s are evaluated. At this point,
all @racket[id]s are bound, so @racket[_patchable-expr]s also creates
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)
(shared ([a a]) (code:comment @#,t{no indirection...})
a)
(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]