diff --git a/collects/scribble/html-render.ss b/collects/scribble/html-render.ss
index 35e94ce9aa..f454eeb8ab 100644
--- a/collects/scribble/html-render.ss
+++ b/collects/scribble/html-render.ss
@@ -272,12 +272,16 @@
(current-subdirectory))
(super get-dest-directory)))
- (define/private (derive-filename d)
- (format "~a.html" (regexp-replace*
- "[^-a-zA-Z0-9_=]"
- (or (format "~a" (part-tag d))
- (content->string (part-title-content d)))
- "_")))
+ (define/private (derive-filename d ht)
+ (let ([fn (format "~a.html" (regexp-replace*
+ "[^-a-zA-Z0-9_=]"
+ (or (format "~a" (part-tag d))
+ (content->string (part-title-content d)
+ this d ht))
+ "_"))])
+ (when ((string-length fn) . >= . 100)
+ (error "file name too long (need a tag):" fn))
+ fn))
(define/override (collect ds fns)
(super collect ds (map (lambda (fn)
@@ -297,7 +301,7 @@
1
(add1 prev-sub))])
(if (= 1 prev-sub)
- (let ([filename (derive-filename d)])
+ (let ([filename (derive-filename d ht)])
(parameterize ([current-output-file (build-path (path-only (current-output-file))
filename)])
(super collect-part d parent ht number)))
@@ -417,7 +421,7 @@
(make-element
(if parent
(make-target-url (if prev
- (derive-filename prev)
+ (derive-filename prev ht)
"index.html"))
"nonavigation")
prev-content)
@@ -426,14 +430,14 @@
(if parent
(make-target-url
(if (toc-part? parent)
- (derive-filename parent)
+ (derive-filename parent ht)
"index.html"))
"nonavigation")
up-content)
sep-element
(make-element
(if next
- (make-target-url (derive-filename next))
+ (make-target-url (derive-filename next ht))
"nonavigation")
next-content))))))))
d
@@ -447,7 +451,7 @@
(next-separate-page)))
;; Render as just a link, and put the actual
;; content in a new file:
- (let* ([filename (derive-filename d)]
+ (let* ([filename (derive-filename d ht)]
[full-path (build-path (path-only (current-output-file))
filename)])
(parameterize ([on-separate-page #t])
diff --git a/collects/scribblings/guide/define-struct.scrbl b/collects/scribblings/guide/define-struct.scrbl
index 435c1fba6b..8aa67bd484 100644
--- a/collects/scribblings/guide/define-struct.scrbl
+++ b/collects/scribblings/guide/define-struct.scrbl
@@ -39,13 +39,13 @@ are built from @scheme[_struct-id] and the @scheme[_field-id]s:
@itemize{
@item{@schemeidfont{make-}@scheme[_struct-id] : a
- @defterm{constructor} function that takes as many arguments as
+ @deftech{constructor} function that takes as many arguments as
the number of @scheme[_field-id]s, and returns an instance of
the structure type.
@examples[(make-posn 1 2)]}
- @item{@scheme[_struct-id]@schemeidfont{?} : a @defterm{predicate}
+ @item{@scheme[_struct-id]@schemeidfont{?} : a @deftech{predicate}
function that takes a single argument and returns @scheme[#t]
if it is an instance of the structure type, @scheme[#f]
otherwise.
@@ -53,14 +53,14 @@ are built from @scheme[_struct-id] and the @scheme[_field-id]s:
@examples[(posn? 3) (posn? (make-posn 1 2))]}
@item{@scheme[_struct-id]@schemeidfont{-}@scheme[_field-id] : for
- each @scheme[_field-id], an @defterm{accessor} that extracts
+ each @scheme[_field-id], an @deftech{accessor} that extracts
the value of the corresponding field from an instance of the
structure type.
@examples[(posn-x (make-posn 1 2)) (posn-y (make-posn 1 2))]}
@item{@schemeidfont{set-}@scheme[_struct-id]@schemeidfont{-}@scheme[_field-id]@schemeidfont{!} : for
- each @scheme[_field-id], a @defterm{mutator} that sets
+ each @scheme[_field-id], a @deftech{mutator} that sets
the value of the corresponding field in an instance of the
structure type.
@@ -68,6 +68,12 @@ are built from @scheme[_struct-id] and the @scheme[_field-id]s:
(posn-x p)
(set-posn-x! p 10)
(posn-x p)]}
+
+ @item{@schemeidfont{struct:}@scheme[_struct-id] : a
+ @deftech{structure type descriptor}, which is a value that
+ represents the structure type (as opposed to a single instance)
+ for certain reflective operations.}
+
}
A @scheme[define-struct] form places no constraints on the kinds of
@@ -76,7 +82,7 @@ type. For example, @scheme[(make-posn "apple" #f)] produces an
instance of @scheme[posn], even though @scheme["apple"] and
@scheme[#f] are not valid coordinates for the obvious uses of
@scheme[posn] instances. Enforcing constraints on field values, such
-as requiring them to be numbers, is the job of a contract, as
+as requiring them to be numbers, is normally the job of a contract, as
discussed later in @secref["guide:contracts"].
@; ------------------------------------------------------------
@@ -114,7 +120,7 @@ p
]
@; ------------------------------------------------------------
-@section{Opaque versus Transparent Stucture Types}
+@section[#:tag "guide:trans-struct"]{Opaque versus Transparent Stucture Types}
With a structure type definition like
@@ -141,11 +147,176 @@ field-name sequence:
An instance of a transparent structure type prints like a vector, and
it shows the content of the structure's fields. A transparent
structure type also allows reflective operations, like
-@scheme[struct?] and @scheme[struct-info], to be used on its
-instances (see @secref["reflection"]).
+@scheme[struct?] and @scheme[struct-info], to be used on its instances
+(see @secref["reflection"]). Different values for @scheme[#:inspector]
+support more controlled access to reflective operations.
Structure types are opaque by default, because opaque structure
instances provide more encapsulation guarantees. That is, a library
can use an opaque structure to encapsulate data, and clients of the
library cannot manipulate the data in the structure except as allowed
by the library.
+
+@; ------------------------------------------------------------
+@section{More Structure Type Options}
+
+The full syntax of @scheme[define-struct] supports many options, both
+at the structure-type level and at the level of individual fields:
+
+@specform/subs[(define-struct id-maybe-super (field ...)
+ struct-option ...)
+ ([id-maybe-super struct-id
+ (struct-id super-id)]
+ [field field-id
+ [field-id field-option ...]])]
+
+A @scheme[_struct-option] always starts with a keyword:
+
+ @specspecsubform[#:immutable]{
+
+ Causes all fields of the structure type to be immutable, and
+ supresses the definition of @tech{mutator} procedures.
+
+ @defexamples[(define-struct fixed-posn (x y) #:immutable)
+ (set-fixed-posn-x! (make-fixed-posn 1 2) 0)]
+
+ Th @scheme[#:immutable] option can also be used as a
+ @scheme[_field-option], in which case it makes an individual field
+ immutable.
+
+ @defexamples[
+ (define-struct person ([name #:immutable] age))
+ (define friend (make-person "Barney" 5))
+ (set-person-age! friend 6)
+ (set-person-name! friend "Mary")]}
+
+ @specspecsubform[(code:line #:inspector inspector-expr)]{
+ Controls reflective access to structure instances, as discussed
+ in the previous section (@secref["guide:trans-struct"]).
+ }
+
+ @specspecsubform[(code:line #:auto-value auto-expr)]{
+
+ Specifies a value to be used for all automatic fields in the
+ structure type, where an automatic field is indicated by the
+ @scheme[#:auto] @scheme[_field-option]. The structure type's
+ constructor omits arguments for automatic fields.
+
+ @defexamples[
+ (define-struct posn (x y [z #:auto])
+ #:inspector #f
+ #:auto-value 0)
+ (make-posn 1 2)
+ ]}
+
+ @specspecsubform[(code:line #:guard guard-expr)]{
+ Specifies a guard procedure to be called whenever an instance of
+ the structure type is created. The guard takes as many arguments
+ as non-automatic fields in the structure type, and it should return
+ the same number of values. The guard can raise an exception if one
+ of the given arguments is unacceptable, or it can convert an
+ argument.
+
+ @defexamples[
+ (define-struct thing (name)
+ #:inspector #f
+ #:guard (lambda (name type-name)
+ (cond
+ [(string? name) name]
+ [(number? name)
+ (number->string name)]
+ [else (error "bad name" name)])))
+ (make-thing "apple")
+ (make-thing 1/2)
+ (make-thing #f)]
+
+ Unlike the constructor for a procedure type, the guard is called even when
+ subtype instances are created. In that case, only the fields accepted by
+ the constructor are provided to the guard (but the subtype's guard gets
+ both the original fields and fields added by the subtype).
+
+ @defexamples[
+ (define-struct (person thing) (age)
+ #:inspector #f
+ #:guard (lambda (name age type-name)
+ (if (negative? age)
+ (error "bad age" age)
+ (values name age))))
+ (make-person "John" 10)
+ (make-person "Mary" -1)
+ (make-person #f 10)]}
+
+ @specspecsubform[(code:line #:property prop-expr val-exr)]{
+ Associates a property and value with the structure type. For
+ example, the @scheme[prop:procedure] property allows a structure
+ instance to be used as a function; the property value determines
+ how a call is implemented when using the structure as a function.
+
+ @defexamples[
+ (define-struct greeter (name)
+ #:property prop:procedure
+ (lambda (self other)
+ (string-append
+ "Hi " other
+ ", I'm " (greeter-name self))))
+ (define joe-greet (make-greeter "Joe"))
+ (greeter-name joe-greet)
+ (joe-greet "Mary")
+ (joe-greet "John")]}
+
+ @specspecsubform[(code:line #:super super-expr)]{
+
+ An alternative to supplying a @scheme[super-id] next to
+ @scheme[struct-id]. Instead of the name of a structure type (which is
+ not an expression), @scheme[super-expr] should produce a
+ @tech{structure type descriptor} value. An advantage of
+ @scheme[#:super] is that structure type descriptors are values, so
+ they can be passed to procedures.
+
+ @defexamples[
+ (define (make-raven-constructor super-type)
+ (define-struct raven ()
+ #:super super-type
+ #:inspector #f
+ #:property prop:procedure (lambda (self)
+ 'nevermore))
+ make-raven)
+ (let ([r ((make-raven-constructor struct:posn) 1 2)])
+ (list r (r)))
+ (let ([r ((make-raven-constructor struct:thing) "apple")])
+ (list r (r)))]}
+
+
+@; ------------------------------------------------------------
+@section{Structure Type Generativity}
+
+Each time that a @scheme[define-struct] form is evaluated, it
+generates a structure type that is distinct from all existing
+structure types, even if some other structure type has the same name
+and fields.
+
+This generativity is useful for enforcing abstractions and
+implementing programs such as interpreters, but beware of placing a
+@scheme[define-struct] form in positions that are evaluated multiple
+times.
+
+@defexamples[
+(define (add-bigger-fish lst)
+ (define-struct fish (size) #:inspector #f)
+ (cond
+ [(null? lst) (list (make-fish 1))]
+ [else (cons (make-fish (* 2 (fish-size (car lst))))
+ lst)]))
+
+(add-bigger-fish null)
+(add-bigger-fish (add-bigger-fish null))
+]
+@defs+int[
+[(define-struct fish (size) #:inspector #f)
+ (define (add-bigger-fish lst)
+ (cond
+ [(null? lst) (list (make-fish 1))]
+ [else (cons (make-fish (* 2 (fish-size (car lst))))
+ lst)]))]
+(add-bigger-fish (add-bigger-fish null))
+]
diff --git a/collects/scribblings/reference/reference.scrbl b/collects/scribblings/reference/reference.scrbl
index 30ccc6ba1b..e6f11aa7f5 100644
--- a/collects/scribblings/reference/reference.scrbl
+++ b/collects/scribblings/reference/reference.scrbl
@@ -17,6 +17,7 @@ language.
@include-section["syntax.scrbl"]
@include-section["derived.scrbl"]
@include-section["data.scrbl"]
+@include-section["struct.scrbl"]
@;------------------------------------------------------------------------
diff --git a/collects/scribblings/reference/struct.scrbl b/collects/scribblings/reference/struct.scrbl
new file mode 100644
index 0000000000..6b77c1274d
--- /dev/null
+++ b/collects/scribblings/reference/struct.scrbl
@@ -0,0 +1,196 @@
+#reader(lib "docreader.ss" "scribble")
+@require["mz.ss"]
+
+@title[#:tag "mz:structures"]{Structures}
+
+A @pidefterm{structure type} is a record datatype composing a number
+of @idefterm{fields}. A @pidefterm{structure}, an instance of a
+structure type, is a first-class value that contains a value for each
+field of the structure type. A structure instance is created with a
+type-specific @tech{constructor} procedure, and its field values are
+accessed and changed with type-specific @tech{accessor} and
+@tech{mutator} procedures. In addition, each structure type has a
+@tech{predicate} procedure that answers @scheme[#t] for instances of
+the structure type and @scheme[#f] for any other value.
+
+A structure type's fields are essentially unnamed, though names are
+supported for error-reporting purposes. The constructor procedure
+takes one value for each field of the structure type, except that some
+of the fields of a structure type can be @deftech{automatic fields};
+the @tech{automatic fields} are initialized to a constant that is
+associated with the structure type, and the corresponding arguments
+are omitted for the constructor procedure. All automatic fields in a
+structure type follow the non-automatic fields.
+
+A structure type can be created as a @pidefterm{structure subtype} of
+an existing base structure type. An instance of a structure subtype
+can always be used as an instance of the base structure type, but the
+subtype gets its own predicate procedure, and it may have its own
+fields in addition to the fields of the base type.
+
+A structure subtype ``inherits'' the fields of its base type. If the
+base type has @math{m} fields, and if @math{n} fields are specified
+for the new structure subtype, then the resulting structure type has
+@math{m+n} fields. The value for automatic fields can be different in
+a subtype than in its base type.
+
+If @math{m'} of the original @math{m} fields are non-automatic (where
+@math{m'symbol (format "make-~a" name))
+ "second field must be a number"))
+ (values a1 (exact->inexact a2) b1))))
+(make-c 'x 'y 'z)
+(define a-c (make-c 'x 2 'z))
+(a-ref a-c 1)
+]}
+
+@defproc[(make-struct-field-accessor [accessor-proc procedure?]
+ [field-pos-k exact-nonnegative-integer?]
+ [field-name symbol?])
+ procedure?]{
+
+Returns a field accessor that is equivalent to @scheme[(lambda (s)
+(accessor-proc s field-pos-k))]. The @scheme[accessor-proc] must be
+an @tech{accessor} returned by @scheme[make-struct-type]. The name of the
+resulting procedure for debugging purposes is derived from
+@scheme[field-name] and the name of @scheme[accessor-proc]'s
+structure type.
+
+For examples, see @scheme[make-struct-type].}
+
+@defproc[(make-struct-field-mutator [mutator-proc procedure?]
+ [field-pos-k exact-nonnegative-integer?]
+ [field-name symbol?])
+ procedure?]{
+
+Returns a field mutator that is equivalent to @scheme[(lambda (s v)
+(mutator-proc s field-pos-k v))]. The @scheme[mutator-proc] must be
+a @tech{mutator} returned by @scheme[make-struct-type]. The name of the
+resulting procedure for debugging purposes is derived from
+@scheme[field-name] and the name of @scheme[mutator-proc]'s
+structure type.
+
+For examples, see @scheme[make-struct-type].}
diff --git a/collects/scribblings/reference/syntax.scrbl b/collects/scribblings/reference/syntax.scrbl
index d9160b502a..55f61c113e 100644
--- a/collects/scribblings/reference/syntax.scrbl
+++ b/collects/scribblings/reference/syntax.scrbl
@@ -354,7 +354,7 @@ Like @scheme[lambda], but without support for keyword or optional arguments.
}
@;------------------------------------------------------------------------
-@section{Local Binding: @scheme[let], @scheme[let*], and @scheme[letrec]}
+@section[#:tag "mz:let"]{Local Binding: @scheme[let], @scheme[let*], and @scheme[letrec]}
@defform*[[(let ([id val-expr] ...) body ...+)
(let proc-id ([id init-expr] ...) body ...+)]]{