From d194a8b145624b5722cfe3d2692efae6a47e5f81 Mon Sep 17 00:00:00 2001 From: Matthew Flatt Date: Fri, 30 Nov 2007 21:34:16 +0000 Subject: [PATCH] doc serialize svn: r7875 --- collects/scheme/private/define-struct.ss | 25 +- collects/scheme/serialize.ss | 2 +- .../scribblings/reference/define-struct.scrbl | 17 +- collects/scribblings/reference/io.scrbl | 2 + .../scribblings/reference/serialization.scrbl | 452 ++++++++++++++++++ collects/scribblings/reference/units.scrbl | 3 +- 6 files changed, 487 insertions(+), 14 deletions(-) create mode 100644 collects/scribblings/reference/serialization.scrbl diff --git a/collects/scheme/private/define-struct.ss b/collects/scheme/private/define-struct.ss index 01bb06ecde..04434abc7d 100644 --- a/collects/scheme/private/define-struct.ss +++ b/collects/scheme/private/define-struct.ss @@ -54,15 +54,17 @@ parts))) id)) - (define (bad why kw where) + (define (bad why kw where . alt) (raise-syntax-error #f (format "~a ~a specification~a" why - (syntax-e kw) + (if (string? kw) + kw + (syntax-e kw)) where) stx - kw)) + (if (null? alt) kw (car alt)))) (define (check-exprs orig-n ps) (let loop ([nps (cdr ps)][n orig-n]) @@ -149,7 +151,7 @@ [(eq? '#:super (syntax-e (car p))) (check-exprs 1 p) (when (lookup config '#:super) - (bad "multiple" (car p) "")) + (bad "multiple" (car p) "s")) (when super-id (raise-syntax-error #f @@ -161,11 +163,11 @@ (loop (cddr p) (extend-config config '#:super (cadr p)))] [(memq (syntax-e (car p)) - '(#:inspector #:guard #:auto-value)) + '(#:guard #:auto-value)) (let ([key (syntax-e (car p))]) (check-exprs 1 p) (when (lookup config key) - (bad "multiple" (car p) "")) + (bad "multiple" (car p) "s")) (loop (cddr p) (extend-config config key (cadr p))))] [(eq? '#:property (syntax-e (car p))) @@ -175,6 +177,17 @@ '#:props (cons (cons (cadr p) (caddr p)) (lookup config '#:props))))] + [(eq? '#:inspector (syntax-e (car p))) + (check-exprs 1 p) + (when (lookup config '#:inspector) + (bad "multiple" "#:inspector or #:transparent" "s" (car p))) + (loop (cddr p) + (extend-config config '#:inspector (cadr p)))] + [(eq? '#:transparent (syntax-e (car p))) + (when (lookup config '#:inspector) + (bad "multiple" "#:inspector or #:transparent" "s" (car p))) + (loop (cdr p) + (extend-config config '#:inspector #'#f))] [(memq (syntax-e (car p)) '(#:mutable #:omit-define-values #:omit-define-syntaxes)) (let ([key (syntax-e (car p))]) diff --git a/collects/scheme/serialize.ss b/collects/scheme/serialize.ss index 1da842b767..816b1680f4 100644 --- a/collects/scheme/serialize.ss +++ b/collects/scheme/serialize.ss @@ -168,7 +168,7 @@ (void)))))))) #,@(map (lambda (other-deserialize-id proc-expr cycle-proc-expr) #`(define #,other-deserialize-id - (make-deserialize-info proc-expr cycle-proc-expr))) + (make-deserialize-info #,proc-expr #,cycle-proc-expr))) other-deserialize-ids (syntax->list #'(make-proc-expr ...)) (syntax->list #'(cycle-make-proc-expr ...))) diff --git a/collects/scribblings/reference/define-struct.scrbl b/collects/scribblings/reference/define-struct.scrbl index a565f00394..899a199729 100644 --- a/collects/scribblings/reference/define-struct.scrbl +++ b/collects/scribblings/reference/define-struct.scrbl @@ -1,6 +1,7 @@ #lang scribble/doc @(require "mz.ss" - (for-syntax scheme/base)) + (for-syntax scheme/base) + (for-label scheme/serialize)) @title[#:tag "define-struct"]{Defining Structure Types: @scheme[define-struct]} @@ -14,6 +15,7 @@ [field-id field-option ...]] [struct-option #:mutable (code:line #:super super-expr) + (code:line #:transparent) (code:line #:inspector inspector-expr) (code:line #:auto-value auto-expr) (code:line #:guard guard-expr) @@ -90,10 +92,11 @@ The @scheme[#:inspector], @scheme[#:auto-value], and @scheme[#:guard] options specify an inspector, value for automatic fields, and guard procedure, respectively. See @scheme[make-struct-type] (in @secref["creatingmorestructs"]) for more information on these -properties of a structure type. The @scheme[#:property] option, which -is the only one that can be specified multiple times, attaches a -property value to the structure type; see @secref["structprops"] -for more information on properties. +properties of a structure type. The @scheme[#:transparent] option is a +shorthand for @scheme[#:inspector #f]. The @scheme[#:property] +option, which is the only one that can be specified multiple times, +attaches a property value to the structure type; see +@secref["structprops"] for more information on properties. If the @scheme[#:omit-define-syntaxes] option is supplied, then @scheme[id] is not bound as a transformer. If the @@ -114,10 +117,12 @@ error is reported. If any @scheme[field-option] or @scheme[struct-option] keyword is repeated, other than @scheme[#:property], a syntax error is reported. +For serialization, see @scheme[define-serializable-struct]. + @defexamples[ (define-struct posn (x y [z #:auto]) #:auto-value 0 - #:inspector #f) + #:transparent) (make-posn 1 2) (posn? (make-posn 1 2)) (posn-y (make-posn 1 2)) diff --git a/collects/scribblings/reference/io.scrbl b/collects/scribblings/reference/io.scrbl index 25f3379a7e..90cf361e8d 100644 --- a/collects/scribblings/reference/io.scrbl +++ b/collects/scribblings/reference/io.scrbl @@ -15,3 +15,5 @@ @include-section["pretty-print.scrbl"] @include-section["readtables.scrbl"] @include-section["custom-write.scrbl"] +@include-section["serialization.scrbl"] + diff --git a/collects/scribblings/reference/serialization.scrbl b/collects/scribblings/reference/serialization.scrbl new file mode 100644 index 0000000000..f9e05a0b2f --- /dev/null +++ b/collects/scribblings/reference/serialization.scrbl @@ -0,0 +1,452 @@ +#lang scribble/doc +@require["mz.ss" + scheme/serialize + (for-label scheme/serialize)] + +@title[#:tag "serialization"]{Serialization} + +@declare-exporting[scheme/serialize] +@note-lib-only[scheme/serialize] + +@defproc[(serializable? [v any/c]) boolean?]{ + +Returns @scheme[#t] if @scheme[v] appears to be serializable, without +checking the content of compound values, and @scheme[#f] otherwise. +See @scheme[serialize] for an enumeration of serializable values.} + +@; ---------------------------------------------------------------------- + +@defproc[(serialize [v serializable?]) any]{ + +Returns a value that encapsulates the value @scheme[v]. This value +includes only readable values, so it can be written to a stream with +@scheme[write], later read from a stream using @scheme[read], and then +converted to a value like the original using +@scheme[deserialize]. Serialization followed by deserialization +produces a value with the same graph structure and mutability as the +original value, but the serialized value is a plain tree (i.e., no +sharing). + +The following kinds of values are serializable: + +@itemize{ + + @item{structures created through @scheme[define-serializable-struct] or + @scheme[define-serializable-struct/version], or more generally + structures with the @scheme[prop:serializable] property (see + @scheme[prop:serializable] for more information);} + + @item{instances of classes defined with @scheme[define-serializable-class] + or @scheme[define-serializable-class];} + + @item{booleans, numbers, characters, symbols, strings, byte strings, + paths (for a specific convention), @|void-const|, and the empty list;} + + @item{pairs, mutable pairs, vectors, boxes, and hash tables; and} + + @item{@scheme[date] and @scheme[arity-at-least] structures.} + +} + +Serialization succeeds for a compound value, such as a pair, only if +all content of the value is serializable. If a value given to +@scheme[serialize] is not completely serializable, the +@exnraise[exn:fail:contract]. + +See @scheme[deserialize] for information on the format of serialized +data.} + +@; ---------------------------------------------------------------------- + +@defproc[(deserialize [v any/c]) any]{ + +Given a value @scheme[v] that was produced by @scheme[serialize], +produces a value like the one given to @scheme[serialize], including +the same graph structure and mutability. + +A serialized representation @scheme[v] is a list of six or seven +elements: + +@itemize{ + + @item{An optional list @scheme['(1)] that represents the version of + the serialization format. If the first element of a + representation \var{v} is not a list, then the version is + @scheme[0]. Version 1 adds support for mutable pairs.} + + @item{A non-negative exact integer @scheme[_s-count] that represents the + number of distinct structure types represented in the + serialized data.} + + @item{A list @scheme[_s-types] of length @scheme[_s-count], where each + element represents a structure type. Each structure type is + encoded as a pair. The @scheme[car] of the pair is @scheme[#f] + for a structure whose deserialization information is defined at + the top level, otherwise it is a quoted @tech{module path} or a byte + string (to be converted into a platform-specific path using + @scheme[bytes->path]) for a module that exports the structure's + deserialization information. The @scheme[cdr] of the pair is + the name of a binding (at the top level or exported from a + module) for deserialization information. These two are used + with either @scheme[namespace-variable-binding] or + @scheme[dynamic-require] to obtain deserialization + information. See @scheme[make-deserialization-info] for more + information on the binding's value.} + + @item{A non-negative exact integer, @scheme[_g-count] that represents the + number of graph points contained in the following list.} + + @item{A list @scheme[_graph] of length @scheme[_g-count], where each element + represents a serialized value to be referenced during the + construction of other serialized values. Each list element is + either a box or not: + + @itemize{ + + @item{A box represents a value that is part of a cycle, and for + deserialization, it must be allocated with @scheme[#f] for + each of its fields. The content of the box indicates the + shape of the value: + + @itemize{ + + @item{a non-negative exact integer @scheme[_i] for an instance + of a structure type that is represented by the + @scheme[_i]th element of the @scheme[_s-types] list;} + + @item{@scheme['c] for a pair, which fails on + deserialization (since pairs are immutable; this + case does not appear in output generated by + @scheme[serialize]);} + + @item{@scheme['m] for a mutable pair;} + + @item{@scheme['b] for a box;} + + @item{a pair whose @scheme[car] is @scheme['v] and whose + @scheme[cdr] is a non-negative exact integer @scheme[_s] + for a vector of length @scheme[_s]; or} + + @item{a list whose first element is @scheme['h] and whose + remaining elements are flags for + @scheme[make-hash-table] for a hash table.} + + @item{@scheme['date] for a @scheme[date] structure, which + fails on deserialization (since dates are immutable; + this case does not appear in output generated by + @scheme[serialize]); or} + + @item{@scheme['arity-at-least] for an + @scheme[arity-at-least] structure, which fails on + deserialization (since dates are immutable; this + case does not appear in output generated by + @scheme[serialize]).} + + } + + The @scheme[#f]-filled value will be updated with content specified + by the fifth element of the serialization list @scheme[v].} + + @item{A non-box represents a @defterm{serial} value to be + constructed immediately, and it is one of the following: + + @itemize{ + + @item{a boolean, number, character, symbol, or empty list, + representing itself.} + + @item{a string, representing an immutable string.} + + @item{a byte string, representing an immutable byte + string.} + + @item{a pair whose @scheme[car] is @scheme['?] and whose + @scheme[cdr] is a non-negative exact integer + @scheme[_i]; it represents the value constructed for the + @scheme[_i]th element of @scheme[_graph], where @scheme[_i] is + less than the position of this element within + @scheme[_graph].} + + @item{a pair whose @scheme[car] is a number @scheme[_i]; it + represents an instance of a structure type that is + described by the @scheme[_i]th element of the + @scheme[_s-types] list. The @scheme[cdr] of the pair is + a list of serials representing arguments to be + provided to the structure type's deserializer.} + + @item{a pair whose @scheme[car] is @scheme['void], + representing @|void-const|.} + + @item{a pair whose @scheme[car] is @scheme['u] and whose + @scheme[cdr] is either a byte string or character + string; it represents a mutable byte or character + string.} + + @item{a pair whose @scheme[car] is @scheme['p] and whose + @scheme[cdr] is a byte string; it represents a + path using the serializer's path convention + (deprecated in favor of @scheme['p+]).} + + @item{a pair whose @scheme[car] is @scheme['p+], whose + @scheme[cadr] is a byte string, and whose @scheme[cddr] + is one of the possible symbol results of + @scheme[system-path-convetion-type]; it represents a + path using the specified convention.} + + @item{a pair whose @scheme[car] is @scheme['c] and whose + @scheme[cdr] is a pair of serials; it represents an + immutable pair.} + + @item{a pair whose @scheme[car] is @scheme['c!] and whose + @scheme[cdr] is a pair of serials; it represents a + pair (but formerly presented a mutable pair), and + does not appear in output generated by + @scheme[serialize].} + + @item{a pair whose @scheme[car] is @scheme['m] and whose + @scheme[cdr] is a pair of serials; it represents a + mutable pair.} + + @item{a pair whose @scheme[car] is @scheme['v] and whose + @scheme[cdr] is a list of serials; it represents an + immutable vector.} + + @item{a pair whose @scheme[car] is @scheme['v!] and whose + @scheme[cdr] is a list of serials; it represents a + mutable vector.} + + @item{a pair whose @scheme[car] is @scheme['b] and whose + @scheme[cdr] is a serial; it represents an immutable + box.} + + @item{a pair whose @scheme[car] is @scheme['b!] and whose + @scheme[cdr] is a serial; it represents a mutable + box.} + + @item{a pair whose @scheme[car] is @scheme['h], whose + @scheme[cadr] is either @scheme['!] or @scheme['-] + (mutable or immutable, respectively), whose + @scheme[caddr] is a list of symbols to be used as + flags for @scheme[make-hash-table], and whose + @scheme[cdddr] is a list of pairs, where the + @scheme[car] of each pair is a serial for a + hash-table key and the @scheme[cdr] is a serial for + the corresponding value.} + + @item{a pair whose @scheme[car] is @scheme['date] and whose + @scheme[cdr] is a list of serials; it represents a + @scheme[date] structure.} + + @item{a pair whose @scheme[car] is @scheme['arity-at-least] + and whose @scheme[cdr] is a serial; it represents an + @scheme[arity-at-least] structure.} + + }} + + }} + + @item{A list of pairs, where the @scheme[car] of each pair is a + non-negative exact integer @scheme[_i] and the @scheme[cdr] is a + serial (as defined in the previous bullet). Each element + represents an update to an @scheme[_i]th element of @scheme[_graph] + that was specified as a box, and the serial describes how to + construct a new value with the same shape as specified by the + box. The content of this new value must be transferred into the + value created for the box in @scheme[_graph].} + + @item{A final serial (as defined in the two bullets back) + representing the result of @scheme[deserialize].} + +} + +The result of @scheme[deserialize] shares no mutable values with the +argument to @scheme[deserialize]. + +If a value provided to @scheme[serialize] is a simple tree (i.e., no +sharing), then the fourth and fifth elements in the serialized +representation will be empty.} + +@; ---------------------------------------------------------------------- + +@defform[(define-serializable-struct id-maybe-super (field ...) + struct-option ...)]{ + +Like @scheme[define-struct], but instances of the structure type are +serializable with @scheme[serialize]. This form is allowed only at +the top level or in a module's top level (so that deserialization +information can be found later). + +Serialization only supports cycles involving the created structure +type when all fields are mutable (or when the cycle can be broken +through some other mutable value). + +In addition to the bindings generated by @scheme[define-struct], +@scheme[define-serializable-struct] binds +@schemeidfont{deserialize-info:}@scheme[_id]@schemeidfont{-v0} to +deserialization information. Furthermore, in a module context, it +automatically @scheme[provide]s this binding. + +The @scheme[define-serializable-struct] form enables the construction +of structure instances from places where +@schemeidfont{make}@scheme[id] is not accessible, since +deserialization must construct instances. Furthermore, +@scheme[define-serializable-struct] provides limited access to field +mutation, but only for instances generated through the deserialization +information bound to +@schemeidfont{deserialize-info:}@scheme[_id]@schemeidfont{-v0}. See +@scheme[make-deserialize-info] for more information. + +The @scheme[-v0] suffix on the deserialization enables future +versioning on the structure type through +@scheme[define-serializable-struct/version]. + +When a supertype is supplied in @scheme[id-maybe-super] is supplied, +compile-time information bound to the supertype identifier must +include all of the supertype's field accessors. If any field mutator +is missing, the structure type will be treated as immutable for the +purposes of marshaling (so cycles involving only instances of the +structure type cannot be handled by the deserializer). + +@examples[ +(define-serializable-struct point (x y)) +(point-x (deserialize (serialize (make-point 1 2)))) +]} + +@; ---------------------------------------------------------------------- + +@defform/subs[(define-serializable-struct/versions id-maybe-super vers (field ...) + (other-version-clause ...) + struct-option ...) + ([other-version-clause (other-vers make-proc-expr + cycle-make-proc-expr)])]{ + +Like @scheme[define-serializable-struct], but the generated +deserializer binding is +@schemeidfont{deserialize-info:}@scheme[_id]@schemeidfont{-v}@scheme[vers]. In +addition, +@schemeidfont{deserialize-info:}@scheme[_id]@schemeidfont{-v}@scheme[other-vers] +is bound for each @scheme[other-vers]. The @scheme[vers] and each +@scheme[other-vers] must be a literal, exact, nonnegative integer. + +Each @scheme[make-proc-expr] should produce a procedure, and the +procedure should accept as many argument as fields in the +corresponding version of the structure type, and it produce an +instance of @scheme[id]. Each @scheme[graph-make-proc-expr] should +produce a procedure of no arguments; this procedure should return two +values: an instance @scheme[x] of @scheme[id] (typically with +@scheme[#f] for all fields) and a procedure that accepts another +instance of @scheme[id] and copies its field values into @scheme[x]. + +@examples[ +(define-serializable-struct point (x y) #:mutable #:transparent) +(define ps (serialize (make-point 1 2))) +(deserialize ps) + +(define x (make-point 1 10)) +(set-point-x! x x) +(define xs (serialize x)) +(deserialize xs) + +(define-serializable-struct/versions point 1 (x y z) + ([0 + (code:comment #, @t{Constructor for simple v0 instances:}) + (lambda (x y) (make-point x y 0)) + (code:comment #, @t{Constructor for v0 instance in a cycle:}) + (lambda () + (let ([p0 (make-point #f #f 0)]) + (values + p0 + (lambda (p) + (set-point-x! p0 (point-x p)) + (set-point-y! p0 (point-y p))))))]) + #:mutable #:transparent) +(deserialize (serialize (make-point 4 5 6))) +(deserialize ps) +(deserialize xs) +]} + +@; ---------------------------------------------------------------------- + +@defproc[(make-deserialize-info [make procedure?] + [cycle-make (() . ->* . (any/c procedure?))]) + any]{ + +Produces a deserialization information record to be used by +@scheme[deserialize]. This information is normally tied to a +particular structure because the structure has a +@scheme[prop:serializable] property value that points to a top-level +variable or module-exported variable that is bound to deserialization +information. + +The @scheme[make] procedure should accept as many argument as the +structure's serializer put into a vector; normally, this is the number +of fields in the structure. It should return an instance of the +structure. + +The @scheme[cycle-make] procedure should accept no arguments, and it +should return two values: a structure instance @scheme[x] (with dummy +field values) and an update procedure. The update procedure takes +another structure instance generated by the @scheme[make], and it +transfers the field values of this instance into @scheme[x].} + +@; ---------------------------------------------------------------------- + +@defthing[prop:serializable property?]{ + +This property identifies structures and structure types that are +serializable. The property value should be constructed with +@scheme[make-serialize-info].} + +@; ---------------------------------------------------------------------- + +@defproc[(make-serialize-info [to-vector (any/c . -> . vector?)] + [deserialize-id (or identifier? + symbol? + (cons/c symbol? + module-path-index?))] + [can-cycle? any/c] + [dir path-string?]) + any]{ + +Produces a value to be associated with a structure type through the +@scheme[prop:serializable] property. This value is used by +@scheme[serialize]. + +The @scheme[to-vector] procedure should accept a structure instance +and produce a vector for the instance's content. + +The @scheme[deserialize-id] value indicates a binding for deserialize +information, to either a module export or a top-level definition. It +must be one of the following: + +@itemize{ + + @item{If @scheme[deserialize-id] is an identifier, and if + @scheme[(identifier-binding deserialize-id)] produces a list, then + the third element is used for the exporting module, otherwise the + top-level is assumed. In either case, @scheme[syntax-e] is used to + obtain the name of an exported identifier or top-level definition.} + + @item{If @scheme[deserialize-id] is a symbol, it indicates a + top-level variable that is named by the symbol.} + + @item{If @scheme[deserialize-id] is a pair, the @scheme[car] must be + a symbol to name an exported identifier, and the @scheme[cdr] must be + a module path index to specify the exporting module.} + +} + +See @scheme[make-deserialize-info] and @scheme[deserialize] for more +information. + +The @scheme[can-cycle?] argument should be false if instances should +not be serialized in such a way that deserialization requires creating +a structure instance with dummy field values and then updating the +instance later. + +The @scheme[dir] argument should be a directory path that is used to +resolve a module reference for the binding of @scheme[deserialize-id]. +This directory path is used as a last resort when +@scheme[deserialize-id] indicates a module that was loaded through a +relative path with respect to the top level. Usually, it should be +@scheme[(or (current-load-relative-directory) (current-directory))].} diff --git a/collects/scribblings/reference/units.scrbl b/collects/scribblings/reference/units.scrbl index e15fae30d5..6263dd95cc 100644 --- a/collects/scribblings/reference/units.scrbl +++ b/collects/scribblings/reference/units.scrbl @@ -397,7 +397,8 @@ See @scheme[unit] for information on @scheme[tagged-sig-spec], sig-id] [infer-linkage-decl - ((link-binding ...) unit-id tagged-link-id) + ((link-binding ...) unit-id + tagged-link-id) unit-id])]{ Like @scheme[compound-unit]. Syntactically, the difference between