From a0f65ba33efbd2f7e5f6c2fa549d55117a986946 Mon Sep 17 00:00:00 2001 From: Matthew Flatt Date: Mon, 21 Apr 2008 17:26:35 +0000 Subject: [PATCH] dictionaries svn: r9383 --- collects/r6rs/scribblings/r6rs.scrbl | 2 + collects/rnrs/hashtables-6.ss | 20 +- collects/scheme/dict.ss | 517 ++++++++++++ collects/scheme/main.ss | 2 + collects/scheme/match/reorder.ss | 2 +- collects/scheme/private/for.ss | 14 +- collects/scribble/eval.ss | 12 + collects/scribblings/reference/booleans.scrbl | 137 ++++ collects/scribblings/reference/bytes.scrbl | 2 +- collects/scribblings/reference/data.scrbl | 568 +------------ collects/scribblings/reference/dicts.scrbl | 468 +++++++++++ .../scribblings/reference/eval-model.scrbl | 772 +++++++++++++++++ collects/scribblings/reference/for.scrbl | 23 +- collects/scribblings/reference/hashes.scrbl | 273 +++++++ collects/scribblings/reference/model.scrbl | 773 +----------------- .../scribblings/reference/reference.scrbl | 1 - collects/scribblings/reference/strings.scrbl | 2 +- collects/scribblings/reference/struct.scrbl | 8 +- collects/scribblings/reference/vectors.scrbl | 145 ++++ collects/tests/mzscheme/dict.ss | 109 +++ src/mzscheme/src/list.c | 6 +- 21 files changed, 2508 insertions(+), 1348 deletions(-) create mode 100644 collects/scheme/dict.ss create mode 100644 collects/scribblings/reference/booleans.scrbl create mode 100644 collects/scribblings/reference/dicts.scrbl create mode 100644 collects/scribblings/reference/eval-model.scrbl create mode 100644 collects/scribblings/reference/hashes.scrbl create mode 100644 collects/scribblings/reference/vectors.scrbl create mode 100644 collects/tests/mzscheme/dict.ss diff --git a/collects/r6rs/scribblings/r6rs.scrbl b/collects/r6rs/scribblings/r6rs.scrbl index 912bce42ab..a7b66295fd 100644 --- a/collects/r6rs/scribblings/r6rs.scrbl +++ b/collects/r6rs/scribblings/r6rs.scrbl @@ -946,6 +946,8 @@ operations that expect textual ports. (hashtable-clear! #f "r6rs-lib-Z-H-14.html" "node_idx_1204") (equal-hash #f "r6rs-lib-Z-H-14.html" "node_idx_1218")] +A hashtable is a dictionary in the sense of @schememodname[scheme/dict]. + @r6rs-module[rnrs/enums-6 (rnrs enums (6)) "r6rs-lib-Z-H-15.html" "node_idx_1226" "Enumerations" (make-enumeration #f "r6rs-lib-Z-H-15.html" "node_idx_1236") diff --git a/collects/rnrs/hashtables-6.ss b/collects/rnrs/hashtables-6.ss index 6d1c7f55c9..ac422b63d9 100644 --- a/collects/rnrs/hashtables-6.ss +++ b/collects/rnrs/hashtables-6.ss @@ -1,5 +1,7 @@ #lang scheme/base +(require scheme/dict) + (provide make-eq-hashtable make-eqv-hashtable (rename-out [r6rs:make-hashtable make-hashtable]) @@ -27,7 +29,23 @@ unwrap mutable? equivalence-function - hash-function)) + hash-function) + #:property prop:dict + (vector (case-lambda + [(ht key) + (hash-ref (hashtable-ht ht) ((hashtable-wrap ht) key))] + [(ht key default) + (hash-ref (hashtable-ht ht) ((hashtable-wrap ht) key) default)]) + (lambda (ht key val) (hashtable-set! ht key val)) + #f + (lambda (ht key) (hashtable-delete! ht key)) + #f + (lambda (ht) (hashtable-size ht)) + (lambda (ht) (hash-iterate-first (hashtable-ht ht))) + (lambda (ht pos) (hash-iterate-next (hashtable-ht ht) pos)) + (lambda (ht pos) ((hashtable-unwrap ht) + (hash-iterate-key (hashtable-ht ht) pos))) + (lambda (ht pos) (hash-iterate-value (hashtable-ht ht) pos)))) (define-struct eqv-box (val) #:property prop:equal+hash (list diff --git a/collects/scheme/dict.ss b/collects/scheme/dict.ss new file mode 100644 index 0000000000..2d0f2525b9 --- /dev/null +++ b/collects/scheme/dict.ss @@ -0,0 +1,517 @@ +#lang scheme/base + +(require (for-syntax scheme/base)) + +(provide prop:dict + dict? + + dict-mutable? + dict-can-remove-keys? + dict-can-functional-set? + + dict-ref + dict-set! + dict-set + dict-remove! + dict-remove + dict-count + dict-iterate-first + dict-iterate-next + dict-iterate-key + dict-iterate-value + + dict-map + dict-for-each + + in-dict + in-dict-keys + in-dict-values + in-dict-pairs + + (rename-out [create-custom-hash make-custom-hash] + [create-immutable-custom-hash make-immutable-custom-hash]) + make-weak-custom-hash) + +(define-values (prop:dict dict-struct? dict-struct-ref) + (make-struct-type-property 'dict + (lambda (v info) + (unless (and + (vector? v) + (= 10 (vector-length v)) + (let-values ([(ref set! set remove! remove count + iterate-first iterate-next + iterate-key iterate-value) + (vector->values v)]) + (and (procedure? ref) + (and (procedure-arity-includes? ref 2) + (procedure-arity-includes? ref 3)) + (or (not set!) + (and (procedure? set!) + (procedure-arity-includes? set! 3))) + (or (not set) + (and (procedure? set) + (procedure-arity-includes? set 3))) + (or (not remove!) + (and (procedure? remove!) + (procedure-arity-includes? remove! 2))) + (or (not remove) + (and (procedure? remove) + (procedure-arity-includes? remove 2))) + (procedure? count) + (procedure-arity-includes? count 1) + (procedure? iterate-first) + (procedure-arity-includes? iterate-first 1) + (procedure? iterate-next) + (procedure-arity-includes? iterate-next 2) + (procedure? iterate-key) + (procedure-arity-includes? iterate-key 2) + (procedure? iterate-value) + (procedure-arity-includes? iterate-value 2)))) + (raise-type-error 'prop:dict-guard + "vector of dict methods" + v)) + v))) + +(define (get-dict-ref v) + (vector-ref v 0)) +(define (get-dict-set! v) + (vector-ref v 1)) +(define (get-dict-set v) + (vector-ref v 2)) +(define (get-dict-remove! v) + (vector-ref v 3)) +(define (get-dict-remove v) + (vector-ref v 4)) +(define (get-dict-count v) + (vector-ref v 5)) +(define (get-dict-iterate-first v) + (vector-ref v 6)) +(define (get-dict-iterate-next v) + (vector-ref v 7)) +(define (get-dict-iterate-key v) + (vector-ref v 8)) +(define (get-dict-iterate-value v) + (vector-ref v 9)) + +(define (assoc? v) + (and (list? v) (andmap pair? v))) + +(define (dict? v) + (or (hash? v) + (vector? v) + (assoc? v) + (dict-struct? v))) + +(define (dict-mutable? d) + (if (dict? d) + (or (and (or (hash? d) + (vector? d)) + (not (immutable? d))) + (and (dict-struct? d) + (get-dict-set! (dict-struct-ref d)) + #t)) + (raise-type-error 'dict-mutable? "dict" d))) + +(define (dict-can-remove-keys? d) + (if (dict? d) + (or (hash? d) + (assoc? d) + (and (dict-struct? d) + (or (get-dict-remove! (dict-struct-ref d)) + (get-dict-remove (dict-struct-ref d))) + #t)) + (raise-type-error 'dict-can-remove-keys? "dict" d))) + +(define (dict-can-functional-set? d) + (if (dict? d) + (or (and (hash? d) (immutable? d)) + (assoc? d) + (and (dict-struct? d) + (get-dict-set (dict-struct-ref d)) + #t)) + (raise-type-error 'dict-can-functional-set? "dict" d))) + +(define dict-ref + (case-lambda + [(d key) + (cond + [(hash? d) (hash-ref d key)] + [(vector? d) (vector-ref d key)] + [(assoc? d) + (let ([a (assoc key d)]) + (if a + (cdr a) + (raise-mismatch-error 'dict-ref + (format "no value for key: ~e in: " + key) + d)))] + [(dict-struct? d) + ((get-dict-ref (dict-struct-ref d)) d key)] + [else + (raise-type-error 'dict-ref 'dict 0 d key)])] + [(d key default) + (cond + [(hash? d) (hash-ref d key default)] + [(vector? d) (if (and (exact-nonnegative-integer? key) + (key . < . (vector-length d))) + (vector-ref d key) + (if (procedure? default) + (default) + default))] + [(assoc? d) + (let ([a (assoc key d)]) + (if a + (cdr a) + (if (procedure? default) + (default) + default)))] + [(dict-struct? d) + ((get-dict-ref (dict-struct-ref d)) d key default)] + [else + (raise-type-error 'dict-ref 'dict 0 d key default)])])) + +(define (dict-set! d key val) + (cond + [(hash? d) (hash-set! d key val)] + [(vector? d) (vector-set! d key val)] + [(assoc? d) + (raise-type-error 'dict-set! "mutable dict" 0 d key val)] + [(dict-struct? d) + (let ([s! (get-dict-set! (dict-struct-ref d))]) + (if s! + (s! d key val) + (raise-type-error 'dict-set! "mutable dict" 0 d key val)))] + [else + (raise-type-error 'dict-set! "dict" 0 d key val)])) + +(define (dict-set d key val) + (cond + [(hash? d) (hash-set d key val)] + [(vector? d) + (raise-type-error 'dict-set "functional-update dict" 0 d key val)] + [(assoc? d) + (let loop ([xd d]) + (cond + [(null? xd) (list (cons key val))] + [else + (let ([a (car xd)]) + (if (equal? (car a) key) + (cons (cons key val) (cdr xd)) + (cons a (loop (cdr xd)))))]))] + [(dict-struct? d) + (let ([s (get-dict-set (dict-struct-ref d))]) + (if s + (s d key val) + (raise-type-error 'dict-set "functional-update dict" 0 d key val)))] + [else + (raise-type-error 'dict-set "dict" 0 d key val)])) + +(define (dict-remove! d key) + (cond + [(hash? d) (hash-remove! d key)] + [(vector? d) + (raise-type-error 'dict-remove! "dict with removeable keys" 0 d key)] + [(assoc? d) + (raise-type-error 'dict-remove! "mutable dict" 0 d key)] + [(dict-struct? d) + (let ([r! (get-dict-remove! (dict-struct-ref d))]) + (if r! + (r! d key) + (raise-type-error 'dict-remove! "mutable dict with removable keys" 0 d key)))] + [else + (raise-type-error 'dict-remove! "dict" 0 d key)])) + +(define (dict-remove d key) + (cond + [(hash? d) (hash-remove d key)] + [(vector? d) + (raise-type-error 'dict-remove "dict with removeable keys" 0 d key)] + [(assoc? d) + (let loop ([xd d]) + (cond + [(null? xd) null] + [else + (let ([a (car xd)]) + (if (equal? (car a) key) + (cdr xd) + (cons a (loop (cdr xd)))))]))] + [(dict-struct? d) + (let ([s (get-dict-remove (dict-struct-ref d))]) + (if s + (s d key) + (raise-type-error 'dict-remove "dict with functionally removeable keys" 0 d key)))] + [else + (raise-type-error 'dict-remove "dict" 0 d key)])) + +(define (dict-count d) + (cond + [(hash? d) (hash-count d)] + [(vector? d) (vector-length d)] + [(assoc? d) (length d)] + [(dict-struct? d) ((get-dict-count (dict-struct-ref d)) d)] + [else + (raise-type-error 'dict-count "dict" d)])) + +(define-struct assoc-iter (head pos)) + +(define (dict-iterate-first d) + (cond + [(hash? d) (hash-iterate-first d)] + [(vector? d) (if (zero? (vector-length d)) + #f + 0)] + [(assoc? d) (if (null? d) #f (make-assoc-iter d d))] + [(dict-struct? d) ((get-dict-iterate-first (dict-struct-ref d)) d)] + [else + (raise-type-error 'dict-iterate-first "dict" d)])) + +(define (dict-iterate-next d i) + (cond + [(hash? d) (hash-iterate-next d i)] + [(vector? d) (let ([len (vector-length d)]) + (cond + [(and (exact-nonnegative-integer? i) + (i . < . len)) + (let ([i (add1 i)]) + (if (= i len) + #f + i))] + [else + (raise-mismatch-error + 'dict-iterate-next + "invalid iteration position for vector: " + i)]))] + [(and (assoc-iter? i) + (eq? d (assoc-iter-head i))) + (let ([pos (cdr (assoc-iter-pos i))]) + (if (null? pos) + #f + (make-assoc-iter d pos)))] + [(dict-struct? d) ((get-dict-iterate-next (dict-struct-ref d)) d i)] + [(assoc? d) + (raise-mismatch-error + 'dict-iterate-next + "invalid iteration position for association list: " + i)] + [else + (raise-type-error 'dict-iterate-next "dict" d)])) + +(define (dict-iterate-key d i) + (cond + [(hash? d) (hash-iterate-key d i)] + [(vector? d) i] + [(and (assoc-iter? i) (eq? d (assoc-iter-head i))) (caar (assoc-iter-pos i))] + [(dict-struct? d) ((get-dict-iterate-key (dict-struct-ref d)) d i)] + [(assoc? d) + (raise-mismatch-error + 'dict-iterate-key + "invalid iteration position for association list: " + i)] + [else + (raise-type-error 'dict-iterate-key "dict" d)])) + +(define (dict-iterate-value d i) + (cond + [(hash? d) (hash-iterate-value d i)] + [(vector? d) (vector-ref d i)] + [(and (assoc-iter? i) (eq? d (assoc-iter-head i))) (cdar (assoc-iter-pos i))] + [(dict-struct? d) ((get-dict-iterate-value (dict-struct-ref d)) d i)] + [(assoc? d) + (raise-mismatch-error + 'dict-iterate-value + "invalid iteration position for association list: " + i)] + [else + (raise-type-error 'dict-iterate-value "dict" d)])) + +(define-sequence-syntax :in-dict + (lambda () #'in-dict) + (lambda (stx) + (syntax-case stx () + [((key-id val-id) (_ dict-expr)) + #'[(key-id val-id) + (:do-in ([(d) dict-expr]) + (void) + ([i (dict-iterate-first d)]) + i + ([key-id (dict-iterate-key d i)] + [val-id (dict-iterate-value d i)]) + #t + #t + ((dict-iterate-next d i)))]] + [_ #f]))) + +(define (in-dict d) + (make-dict-sequence + d + (lambda (i) + (values (dict-iterate-key d i) + (dict-iterate-value d i))) + (lambda (k v) #t) + (lambda (i k v) #t))) + +(define (in-dict-keys d) + (make-dict-sequence + d + (lambda (i) (dict-iterate-key d i)) + (lambda (k) #t) + (lambda (i k) #t))) + +(define (in-dict-values d) + (make-dict-sequence + d + (lambda (i) (dict-iterate-value d i)) + (lambda (v) #t) + (lambda (i v) #t))) + +(define (in-dict-pairs d) + (make-dict-sequence + d + (lambda (i) + (cons (dict-iterate-key d i) + (dict-iterate-value d i))) + (lambda (p) #t) + (lambda (i p) #t))) + +(define (make-dict-sequence d get val-true val+pos-true) + (make-do-sequence + (lambda () + (values get + (lambda (i) (dict-iterate-next d i)) + (dict-iterate-first d) + (lambda (i) i) + val-true + val+pos-true)))) + +(define (dict-map d f) + (for/list ([(k v) (in-dict d)]) + (f k v))) + +(define (dict-for-each d f) + (for ([(k v) (in-dict d)]) + (f k v))) + +;; ---------------------------------------- + +(define-struct hash-box (key)) + +(define custom-hash-ref + (case-lambda + [(d k) (hash-ref (custom-hash-table d) + ((custom-hash-make-box d) k) + (lambda () + (raise-mismatch-error + 'dict-ref + "no value found for key: " + k)))] + [(d k fail) (hash-ref (custom-hash-table d) + ((custom-hash-make-box d) k) + fail)])) + +(define (custom-hash-set! d k v) + (hash-set! (custom-hash-table d) + ((custom-hash-make-box d) k) + v)) + +(define (custom-hash-set d k v) + (let ([table (hash-set (custom-hash-table d) + ((custom-hash-make-box d) k) + v)]) + (make-immutable-custom-hash table + (custom-hash-make-box d)))) + +(define (custom-hash-remove! d k) + (hash-remove! (custom-hash-table d) + ((custom-hash-make-box d) k))) + +(define (custom-hash-remove d k) + (let ([table (hash-remove (custom-hash-table d) + ((custom-hash-make-box d) k))]) + (make-immutable-custom-hash table + (custom-hash-make-box d)))) + +(define (custom-hash-count d) + (hash-count (custom-hash-table d))) + +(define (custom-hash-iterate-first d) + (hash-iterate-first (custom-hash-table d))) + +(define (custom-hash-iterate-next d i) + (hash-iterate-next (custom-hash-table d) i)) + +(define (custom-hash-iterate-key d i) + (hash-box-key (hash-iterate-key (custom-hash-table d) i))) + +(define (custom-hash-iterate-value d i) + (hash-iterate-value (custom-hash-table d) i)) + +(define-struct custom-hash (table make-box) + #:property prop:dict + (vector custom-hash-ref + custom-hash-set! + #f + custom-hash-remove! + #f + custom-hash-count + custom-hash-iterate-first + custom-hash-iterate-next + custom-hash-iterate-key + custom-hash-iterate-value) + #:property prop:equal+hash + (list (lambda (a b recur) + (and (recur (custom-hash-make-box a) + (custom-hash-make-box b)) + (recur (custom-hash-table a) + (custom-hash-table b)))) + (lambda (a recur) (recur (custom-hash-table a))) + (lambda (a recur) (recur (custom-hash-table a))))) + +(define-struct (immutable-custom-hash custom-hash) () + #:property prop:dict + (vector custom-hash-ref + #f + custom-hash-set + #f + custom-hash-remove + custom-hash-count + custom-hash-iterate-first + custom-hash-iterate-next + custom-hash-iterate-key + custom-hash-iterate-value)) + +(define-values (create-custom-hash + create-immutable-custom-hash + make-weak-custom-hash) + (let ([mk + (lambda (hash hash2 =? who make-custom-hash table) + (unless (and (procedure? =?) + (procedure-arity-includes? =? 2)) + (raise-type-error who "procedure (arity 2)" =?)) + (unless (and (procedure? hash) + (procedure-arity-includes? hash 1)) + (raise-type-error who "procedure (arity 1)" hash)) + (unless (and (procedure? hash2) + (procedure-arity-includes? hash2 1)) + (raise-type-error who "procedure (arity 1)" hash2)) + (let () + (define-struct (box hash-box) () + #:property prop:equal+hash (list + (lambda (a b recur) + (=? (hash-box-key a) (hash-box-key b))) + (lambda (v recur) + (hash (hash-box-key v))) + (lambda (v recur) + (hash2 (hash-box-key v))))) + (make-custom-hash table make-box)))]) + (let ([make-custom-hash + (lambda (=? hash [hash2 (lambda (v) 10001)]) + (mk hash hash2 =? 'make-custom-hash make-custom-hash (make-hash)))] + [make-immutable-custom-hash + (lambda (=? hash [hash2 (lambda (v) 10001)]) + (mk hash hash2 =? 'make-immutable-custom-hash make-immutable-custom-hash #hash()))] + [make-weak-custom-hash + (lambda (=? hash [hash2 (lambda (v) 10001)]) + (mk hash hash2 =? 'make-immutable-custom-hash make-immutable-custom-hash (make-weak-hash)))]) + (values make-custom-hash + make-immutable-custom-hash + make-weak-custom-hash)))) diff --git a/collects/scheme/main.ss b/collects/scheme/main.ss index 07ef68bddf..f62734c422 100644 --- a/collects/scheme/main.ss +++ b/collects/scheme/main.ss @@ -2,6 +2,7 @@ (require scheme/contract scheme/class scheme/unit + scheme/dict scheme/include scheme/pretty scheme/math @@ -24,6 +25,7 @@ (provide (all-from-out scheme/contract scheme/class scheme/unit + scheme/dict scheme/include scheme/pretty scheme/math diff --git a/collects/scheme/match/reorder.ss b/collects/scheme/match/reorder.ss index 8ec4a4bd36..b69c3f5e9c 100644 --- a/collects/scheme/match/reorder.ss +++ b/collects/scheme/match/reorder.ss @@ -26,7 +26,7 @@ (define-sequence-syntax in-par (lambda () (raise-syntax-error 'in-par "bad")) - (lambda (orig-stx stx) + (lambda (stx) (syntax-case stx () [((id) (_ lst-exprs)) #'[(id) diff --git a/collects/scheme/private/for.ss b/collects/scheme/private/for.ss index 512139cc4a..342c2035ac 100644 --- a/collects/scheme/private/for.ss +++ b/collects/scheme/private/for.ss @@ -74,7 +74,7 @@ 0 proc1 proc2)) (unless (and (procedure? proc2) - (procedure-arity-includes? proc2 2)) + (procedure-arity-includes? proc2 1)) (raise-type-error 'define-sequence-syntax "procedure (arity 1)" 1 @@ -165,7 +165,7 @@ (let ([xformer (sequence-transformer-ref m 1)] [introducer (make-syntax-introducer)] [certifier (sequence-transformer-ref m 2)]) - (let ([xformed (xformer orig-stx (introducer (syntax-local-introduce clause)))]) + (let ([xformed (xformer (introducer (syntax-local-introduce clause)))]) (if xformed (expand-clause orig-stx (certify-clause (syntax-local-introduce (introducer xformed)) certifier @@ -863,7 +863,7 @@ (define-sequence-syntax *in-range (lambda () #'in-range) - (lambda (orig-stx stx) + (lambda (stx) (let loop ([stx stx]) (syntax-case stx () [[(id) (_ a b step)] #`[(id) @@ -898,7 +898,7 @@ (define-sequence-syntax *in-naturals (lambda () #'in-naturals) - (lambda (orig-stx stx) + (lambda (stx) (let loop ([stx stx]) (syntax-case stx () [[(id) (_ start)] @@ -929,7 +929,7 @@ (define-sequence-syntax *in-list (lambda () #'in-list) - (lambda (orig-stx stx) + (lambda (stx) (syntax-case stx () [((id) (_ lst-expr)) #'[(id) @@ -956,7 +956,7 @@ vector-length-id in-vector-id vector-ref-id) - (lambda (orig-stx stx) + (lambda (stx) (with-syntax ([vector? vector?-id] [in-vector in-vector-id] [vector-length vector-length-id] @@ -1009,7 +1009,7 @@ (define-sequence-syntax *in-indexed (lambda () #'in-indexed) - (lambda (orig-stx stx) + (lambda (stx) (syntax-case stx () [((id1 id2) (_ gen-expr)) #'[(id1 id2) (in-parallel gen-expr (*in-naturals))]])))) diff --git a/collects/scribble/eval.ss b/collects/scribble/eval.ss index 19d56d2371..f73ddabb89 100644 --- a/collects/scribble/eval.ss +++ b/collects/scribble/eval.ss @@ -195,6 +195,18 @@ (hash-set! ht v v2) (set-box! v2 (copy-value (unbox v) ht)) v2)] + [(hash? v) (let ([ph (make-placeholder #f)]) + (hash-set! ht v ph) + (let ([a (hash-map v (lambda (k v) + (cons (copy-value k ht) + (copy-value v ht))))]) + (placeholder-set! + ph + ((if (hash-eq? v) + make-hasheq-placeholder + make-hash-placeholder) + a))) + ph)] [else v])) (define (strip-comments stx) diff --git a/collects/scribblings/reference/booleans.scrbl b/collects/scribblings/reference/booleans.scrbl new file mode 100644 index 0000000000..3371181002 --- /dev/null +++ b/collects/scribblings/reference/booleans.scrbl @@ -0,0 +1,137 @@ +#lang scribble/doc +@(require "mz.ss") + +@title[#:tag "booleans"]{Booleans and Equality} + +True and false are represented by the values @scheme[#t] and +@scheme[#f], respectively, though operations that depend a boolean +value typically treat anything other than @scheme[#f] as true. + +See also: @scheme[and], @scheme[or], @scheme[andmap], @scheme[ormap]. + + +@defproc[(boolean? [v any/c]) boolean?]{ + +Returns @scheme[#t] if @scheme[v] is @scheme[#t] or @scheme[#f], +@scheme[#f] otherwise.} + + +@defproc[(not [v any/c]) boolean?]{ + +Returns @scheme[#t] if @scheme[v] is @scheme[#f], @scheme[#f] otherwise. +} + + +@defproc[(equal? [v1 any/c] [v2 any/c]) boolean?]{ + +Two values are @scheme[equal?] if and only if they are @scheme[eqv?], +unless otherwise specified for a particular datatype. + +Datatypes with further specification of @scheme[equal?] include +strings, byte strings, numbers, pairs, mutable pairs, vectors, hash +tables, and inspectable structures. In the last five cases, equality +is recursively defined; if both @scheme[v1] and @scheme[v2] contain +reference cycles, they are equal when the infinite unfoldings of the +values would be equal. See also @scheme[prop:equal+hash].} + + +@defproc[(eqv? [v1 any/c] [v2 any/c]) boolean?]{ + +Two values are @scheme[eqv?] if and only if they are @scheme[eq?], +unless otherwise specified for a particular datatype. + +The number and character datatypes are the only ones for which +@scheme[eqv?] differs from @scheme[eq?].} + + +@defproc[(eq? [v1 any/c] [v2 any/c]) boolean?]{ + +Return @scheme[#t] if @scheme[v1] and @scheme[v2] refer to the same +object, @scheme[#f] otherwise. See also @secref["model-eq"].} + + +@defproc[(immutable? [v any/c]) boolean?]{ + +Returns @scheme[#t] if @scheme[v] is an immutable @tech{string}, +@tech{byte string}, @tech{vector}, @tech{hash table}, or box, +@scheme[#f] otherwise.} + +@defthing[prop:equal+hash struct-type-property?]{ + +A @tech{structure type property} (see @secref["structprops"]) that +supplies an equality predicate and hashing functions for a structure +type. The property value must be a list of three procedures: + +@itemize{ + + @item{@scheme[_equal-proc : (any/c any/c (any/c any/c . -> + . boolean?) . -> . any/c)] --- tests whether the first two + arguments are equal, where both values are instances of the + structure type to which the property is associated (or a + subtype of the structure type). + + The third argument is an @scheme[equal?] predicate to use for + recursive equality checks; use the given predicate instead of + @scheme[equal?] to ensure that data cycles are handled + properly. + + The @scheme[_equal-proc] is called for a pair of structures + only when they are not @scheme[eq?], and only when they both + have a @scheme[prop:equal+hash] value inherited from the same + structure type. With this strategy, the order in which + @scheme[equal?] receives two structures does not matter. It + also means that, by default, a structure sub-type inherits the + equality predicate of its parent, if any.} + + @item{@scheme[_hash-proc : (any/c (any/c . -> . exact-integer?) . -> + . exact-integer?)] --- computes a hash code for the given + structure, like @scheme[equal-hash-code]. The first argument is + an instance of the structure type (or one of its subtypes) to + which the property is associated. + + The second argument is a @scheme[equal-hash-code]-like + procedure to use for recursive hash-code computation; use the + given procedure instead of @scheme[equal-hash-code] to ensure + that data cycles are handled properly.} + + @item{@scheme[_hash2-proc : (any/c (any/c . -> . exact-integer?) . -> + . exact-integer?)] --- computes a secondary hash code for the + given structure. This procedure is like @scheme[_hash-proc], + but analogous to @scheme[equal-secondary-hash-code].} + +} + +Take care to ensure that @scheme[_hash-proc] and @scheme[_hash2-proc] +are consistent with @scheme[_equal-proc]. Specifically, +@scheme[_hash-proc] and @scheme[_hash2-proc] should produce the same +value for any two structures for which @scheme[_equal-proc] produces a +true value. + +When a structure type has no @scheme[prop:equal+hash] property, then +transparent structures (i.e., structures with an @tech{inspector} that +is controlled by the current @tech{inspector}) are @scheme[equal?] +when they are instances of the same structure type (not counting +sub-types), and when they have @scheme[equal?] field values. For +transparent structures, @scheme[equal-hash-code] and +@scheme[equal-secondary-hash-code] derive hash code using the field +values. For opaque structure types, @scheme[equal?] is the same as +@scheme[eq?], and @scheme[equal-hash-code] and +@scheme[equal-secondary-hash-code] results are based only on +@scheme[eq-hash-code].} + +@section{Boolean Synonyms} + +@note-lib[scheme/bool] + +@defthing[true boolean?]{A synonym for @scheme[#t].} + +@defthing[false boolean?]{A synonym for @scheme[#f].} + +@defproc[(symbol=? [a symbol?] [b symbol?]) boolean?]{ + +Returns @scheme[(equal? a b)].} + +@defproc[(boolean=? [a boolean?] [b boolean?]) boolean?]{ + +Returns @scheme[(equal? a b)].} + diff --git a/collects/scribblings/reference/bytes.scrbl b/collects/scribblings/reference/bytes.scrbl index b4efad7a3d..6ecbba4f29 100644 --- a/collects/scribblings/reference/bytes.scrbl +++ b/collects/scribblings/reference/bytes.scrbl @@ -7,7 +7,7 @@ @local-table-of-contents[] -A @pidefterm{byte string} is a fixed-length arary of bytes. A +A @deftech{byte string} is a fixed-length arary of bytes. A @pidefterm{byte} is an exact integer between @scheme[0] and @scheme[255] inclusive. diff --git a/collects/scribblings/reference/data.scrbl b/collects/scribblings/reference/data.scrbl index 3d8e2a8d0d..815fca43ef 100644 --- a/collects/scribblings/reference/data.scrbl +++ b/collects/scribblings/reference/data.scrbl @@ -1,147 +1,15 @@ #lang scribble/doc @(require "mz.ss") -@title[#:style 'toc #:tag "data"]{Primitive Datatypes} +@title[#:style 'toc #:tag "data"]{Datatypes} -Each of the built-in datatypes comes with a set of procedures for -manipulating members of the datatype. +Each pre-defined datatype comes with a set of procedures for +manipulating instances of the datatype. @local-table-of-contents[#:style 'immediate-only] @; ------------------------------------------------------------ - -@section[#:tag "booleans"]{Booleans} - -True and false are represented by the values @scheme[#t] and -@scheme[#f], respectively, though operations that depend a boolean -value typically treat anything other than @scheme[#f] as true. - -See also: @scheme[and], @scheme[or], @scheme[andmap], @scheme[ormap]. - - -@defproc[(boolean? [v any/c]) boolean?]{ - -Returns @scheme[#t] if @scheme[v] is @scheme[#t] or @scheme[#f], -@scheme[#f] otherwise.} - - -@defproc[(not [v any/c]) boolean?]{ - -Returns @scheme[#t] if @scheme[v] is @scheme[#f], @scheme[#f] otherwise. -} - - -@defproc[(equal? [v1 any/c] [v2 any/c]) boolean?]{ - -Two values are @scheme[equal?] if and only if they are @scheme[eqv?], -unless otherwise specified for a particular datatype. - -Datatypes with further specification of @scheme[equal?] include -strings, byte strings, numbers, pairs, mutable pairs, vectors, hash -tables, and inspectable structures. In the last five cases, equality -is recursively defined; if both @scheme[v1] and @scheme[v2] contain -reference cycles, they are equal when the infinite unfoldings of the -values would be equal. See also @scheme[prop:equal+hash].} - - -@defproc[(eqv? [v1 any/c] [v2 any/c]) boolean?]{ - -Two values are @scheme[eqv?] if and only if they are @scheme[eq?], -unless otherwise specified for a particular datatype. - -The number and character datatypes are the only ones for which -@scheme[eqv?] differs from @scheme[eq?].} - - -@defproc[(eq? [v1 any/c] [v2 any/c]) boolean?]{ - -Return @scheme[#t] if @scheme[v1] and @scheme[v2] refer to the same -object, @scheme[#f] otherwise. See also @secref["model-eq"].} - - -@defproc[(immutable? [v any/c]) boolean?]{ - -Returns @scheme[#t] if @scheme[v] is an immutable string, byte string, -vector, or box, @scheme[#f] otherwise.} - -@defthing[prop:equal+hash struct-type-property?]{ - -A @tech{structure type property} (see @secref["structprops"]) that -supplies an equality predicate and hashing functions for a structure -type. The property value must be a list of three procedures: - -@itemize{ - - @item{@scheme[_equal-proc : (any/c any/c (any/c any/c . -> - . boolean?) . -> . any/c)] --- tests whether the first two - arguments are equal, where both values are instances of the - structure type to which the property is associated (or a - subtype of the structure type). - - The third argument is an @scheme[equal?] predicate to use for - recursive equality checks; use the given predicate instead of - @scheme[equal?] to ensure that data cycles are handled - properly. - - The @scheme[_equal-proc] is called for a pair of structures - only when they are not @scheme[eq?], and only when they both - have a @scheme[prop:equal+hash] value inherited from the same - structure type. With this strategy, the order in which - @scheme[equal?] receives two structures does not matter. It - also means that, by default, a structure sub-type inherits the - equality predicate of its parent, if any.} - - @item{@scheme[_hash-proc : (any/c (any/c . -> . exact-integer?) . -> - . exact-integer?)] --- computes a hash code for the given - structure, like @scheme[equal-hash-code]. The first argument is - an instance of the structure type (or one of its subtypes) to - which the property is associated. - - The second argument is a @scheme[equal-hash-code]-like - procedure to use for recursive hash-code computation; use the - given procedure instead of @scheme[equal-hash-code] to ensure - that data cycles are handled properly.} - - @item{@scheme[_hash2-proc : (any/c (any/c . -> . exact-integer?) . -> - . exact-integer?)] --- computes a secondary hash code for the - given structure. This procedure is like @scheme[_hash-proc], - but analogous to @scheme[equal-secondary-hash-code].} - -} - -Take care to ensure that @scheme[_hash-proc] and @scheme[_hash2-proc] -are consistent with @scheme[_equal-proc]. Specifically, -@scheme[_hash-proc] and @scheme[_hash2-proc] should produce the same -value for any two structures for which @scheme[_equal-proc] produces a -true value. - -When a structure type has no @scheme[prop:equal+hash] property, then -transparent structures (i.e., structures with an @tech{inspector} that -is controlled by the current @tech{inspector}) are @scheme[equal?] -when they are instances of the same structure type (not counting -sub-types), and when they have @scheme[equal?] field values. For -transparent structures, @scheme[equal-hash-code] and -@scheme[equal-secondary-hash-code] derive hash code using the field -values. For opaque structure types, @scheme[equal?] is the same as -@scheme[eq?], and @scheme[equal-hash-code] and -@scheme[equal-secondary-hash-code] results are based only on -@scheme[eq-hash-code].} - -@subsection{Boolean Synonyms} - -@note-lib[scheme/bool] - -@defthing[true boolean?]{A synonym for @scheme[#t].} - -@defthing[false boolean?]{A synonym for @scheme[#f].} - -@defproc[(symbol=? [a symbol?] [b symbol?]) boolean?]{ - -Returns @scheme[(equal? a b)].} - -@defproc[(boolean=? [a boolean?] [b boolean?]) boolean?]{ - -Returns @scheme[(equal? a b)].} +@include-section["booleans.scrbl"] @; ------------------------------------------------------------ @include-section["numbers.scrbl"] @@ -163,10 +31,10 @@ Returns @scheme[(equal? a b)].} @section-index["symbols" "generating"] @section-index["symbols" "unique"] -A symbol is like an immutable string, but symbols are normally -@deftech{interned}, so that two symbols with the same character -content are normally @scheme[eq?]. All symbols produced by the default -reader (see @secref["parse-symbol"]) are interned. +A @deftech{symbol} is like an immutable string, but symbols are +normally @deftech{interned}, so that two symbols with the same +character content are normally @scheme[eq?]. All symbols produced by +the default reader (see @secref["parse-symbol"]) are interned. The two procedures @scheme[string->uninterned-symbol] and @scheme[gensym] generate @deftech{uninterned} symbols, i.e., symbols @@ -222,8 +90,8 @@ ephemeron key (see @secref["ephemerons"]). @guideintro["keywords"]{keywords} -A keyword is like an @tech{interned} symbol, but its printed form -starts with @litchar{#:}, and a keyword cannot be used as an +A @deftech{keyword} is like an @tech{interned} symbol, but its printed +form starts with @litchar{#:}, and a keyword cannot be used as an identifier. Furthermore, a keyword by itself is not a valid expression, though a keyword can be @scheme[quote]d to form an expression that produces the symbol. @@ -259,148 +127,8 @@ for each pair of keywords is the same as using @; ---------------------------------------------------------------------- @include-section["mpairs.scrbl"] -@; ------------------------------------------------------------ -@section[#:tag "vectors"]{Vectors} - -A vector is a fixed-length array with constant-time access and update -of the vector slots, which are numbered from @scheme[0] to one less -than the number of slots in the vector. - -Two vectors are @scheme[equal?] if they have the same length, and if -the values in corresponding slots of the the vectors are -@scheme[equal?]. - -A vector can be @defterm{mutable} or @defterm{immutable}. When an -immutable vector is provided to a procedure like @scheme[vector-set!], -the @exnraise[exn:fail:contract]. Vectors generated by the default -reader (see @secref["parse-string"]) are immutable. - -A vector can be used as a single-valued sequence (see -@secref["sequences"]). The elements of the vector serve as elements -of the sequence. See also @scheme[in-vector]. - -@defproc[(vector? [v any/c]) boolean?]{ - -Returns @scheme[#t] if @scheme[v] is a vector, @scheme[#f] otherwise.} - - -@defproc[(make-vector [size nonnegative-exact-integer?] - [v any/c 0]) vector?]{ - -Returns a mutable vector with @scheme[size] slots, where all slots are -initialized to contain @scheme[v].} - - -@defproc[(vector [v any/c] ...) vector?]{ - -Returns a mutable vector with as many slots as provided @scheme[v]s, -where the slots are initialized to contain the given @scheme[v]s in -order.} - - -@defproc[(vector-immutable [v any/c] ...) (and/c vector? - immutable?)]{ - -Returns an immutable vector with as many slots as provided -@scheme[v]s, where the slots are contain the given @scheme[v]s in -order.} - - - -@defproc[(vector-length [vec vector?]) nonnegative-exact-integer?]{ - -Returns the length of @scheme[vec] (i.e., the number of slots in the -vector).} - -@defproc[(vector-ref [vec vector?][pos nonnegative-exact-integer?]) any/c]{ - -Returns the element in slot @scheme[pos] of @scheme[vec]. The first -slot is position @scheme[0], and the last slot is one less than -@scheme[(vector-length vec)].} - -@defproc[(vector-set! [vec (and/c vector? (not/c immutable?))] - [pos nonnegative-exact-integer?] - [v any/c]) - void?]{ - -Updates the slot @scheme[pos] of @scheme[vec] to contain @scheme[v].} - - -@defproc[(vector->list [vec vector?]) - list?]{ - -Returns a list with the same length and elements as @scheme[vec].} - - -@defproc[(list->vector [lst list?]) - vector?]{ - -Returns a mutable vector with the same length and elements as -@scheme[lst].} - - -@defproc[(vector->immutable-vector [vec vector?]) - (and/c vector? immutable?)]{ - -Returns an immutable vector with the same length and elements as @scheme[vec]. -If @scheme[vec] is itself immutable, then it is returned as the result.} - - -@defproc[(vector-fill! [vec (and/c vector? (not/c immutable?))] - [v any/c]) - void?]{ - -Changes all slots of @scheme[vec] to contain @scheme[v].} - - -@defproc[(vector-copy! [dest (and/c vector? (not/c immutable?))] - [dest-start exact-nonnegative-integer?] - [src vector?] - [src-start exact-nonnegative-integer? 0] - [src-end exact-nonnegative-integer? (vector-length src)]) - void?]{ - - Changes the elements of @scheme[dest] starting at position - @scheme[dest-start] to match the elements in @scheme[src] from - @scheme[src-start] (inclusive) to @scheme[src-end] (exclusive). The - vectors @scheme[dest] and @scheme[src] can be the same vector, and in - that case the destination region can overlap with the source region; - the destination elements after the copy match the source elements - from before the copy. If any of @scheme[dest-start], - @scheme[src-start], or @scheme[src-end] are out of range (taking into - account the sizes of the vectors and the source and destination - regions), the @exnraise[exn:fail:contract]. - -@examples[(define v (vector 'A 'p 'p 'l 'e)) - (vector-copy! v 4 #(y)) - (vector-copy! v 0 v 3 4) - v]} - - -@defproc[(vector->values [vec vector?] - [start-pos nonnegative-exact-integer? 0] - [end-pos nonnegative-exact-integer? (vector-length vec)]) - any]{ - -Returns @math{@scheme[end-pos] - @scheme[start-pos]} values, which are -the elements of @scheme[vec] from @scheme[start-pos] (inclusive) to -@scheme[end-pos] (exclusive). If @scheme[start-pos] or -@scheme[end-pos] are greater than @scheme[(vector-length vec)], or if -@scheme[end-pos] is less than @scheme[start-pos], the -@exnraise[exn:fail:contract].} - -@defproc[(build-vector [n exact-nonnegative-integer?] - [proc (exact-nonnegative-integer? . -> . any.c)]) - vector?]{ - -Creates a vector of @scheme[n] elements by applying @scheme[proc] to -the integers from @scheme[0] to @scheme[(sub1 n)] in order. If -@scheme[_vec] is the resulting vector, then @scheme[(vector-ref _vec -_i)] is the value produced by @scheme[(proc _i)]. - -@examples[ -(build-vector 5 add1) -]} +@; ---------------------------------------------------------------------- +@include-section["vectors.scrbl"] @; ------------------------------------------------------------ @section[#:tag "boxes"]{Boxes} @@ -431,279 +159,15 @@ Returns the content of @scheme[box]. For any @scheme[v], Sets the content of @scheme[box] to @scheme[v].} - @; ---------------------------------------------------------------------- -@section[#:tag "hashtables"]{Hash Tables} - -A @deftech{hash table} (or simply @deftech{hash}) maps each of its -keys to a single value. For a given hash table, keys are equivalent -via @scheme[equal?] or @scheme[eq?], and keys are retained either -strongly or weakly (see @secref["weakbox"]). A hash table is also -either mutable or immutable; immutable tables support constant-time -functional update. - -A hash table can be used as a two-valued @tech{sequence} (see -@secref["sequences"]). The keys and values of the hash table serve as -elements of the sequence (i.e., each element is a key and its -associated value). If a mapping is added to or removed from the hash -table during iteration, then an iteration step may fail with -@scheme[exn:fail:contract], or the iteration may skip or duplicate -keys and values. See also @scheme[in-hash], @scheme[in-hash-keys], -@scheme[in-hash-values], and @scheme[in-hash-pairs]. - -Two hash tables are @scheme[equal?] if they use the same -key-comparison procedure (@scheme[equal?] or @scheme[eq?]), if they -both hold keys strongly or weakly, and if they have the same -mutability. - -@bold{Caveats concerning concurrent modification:} A mutable hash -table can be manipulated with @scheme[hash-ref], @scheme[hash-set!], -and @scheme[hash-remove!] concurrently by multiple threads, and the -operations are protected by a table-specific semaphore as needed. Two -caveats apply, however: - - @itemize{ - - @item{If a thread is terminated while applying @scheme[hash-ref], - @scheme[hash-set!], or @scheme[hash-remove!] to a hash table that - uses @scheme[equal?] key comparisons, all current and future - operations on the hash table block indefinitely.} - - @item{The @scheme[hash-map] and @scheme[hash-for-each] procedures do - not use the table's semaphore. Consequently, if a hash table is - extended with new keys by another thread while a map or for-each - traversal is in process, arbitrary key--value pairs can be dropped - or duplicated in the traversal. Similarly, if a map or for-each - procedure itself extends the table, arbitrary key--value pairs can - be dropped or duplicated. However, key mappings can be deleted or - remapped by any thread with no adverse affects (i.e., the change - does not affect a traversal if the key has been seen already, - otherwise the traversal skips a deleted key or uses the remapped - key's new value).} - - } - -@bold{Caveat concerning mutable keys:} If a key into an -@scheme[equal?]-based hash table is mutated (e.g., a key string is -modified with @scheme[string-set!]), then the hash table's behavior -for insertion and lookup operations becomes unpredictable. - - -@defproc[(hash? [v any/c]) boolean?]{ - -Returns @scheme[#t] if @scheme[v] is a @tech{hash table}, @scheme[#f] -otherwise.} - -@defproc[(hash-eq? [hash hash?]) boolean?]{ - -Returns @scheme[#t] if @scheme[hash] compares keys with @scheme[eq?], -@scheme[#f] if it compares with @scheme[equal?].} - - -@defproc[(hash-weak? [hash hash?]) boolean?]{ - -Returns @scheme[#t] if @scheme[hash] retains its keys weakly, -@scheme[#f] if it retains keys strongly.} - - -@defproc[(make-hash) hash?]{ - -Creates an empty mutable hash table that holds keys strongly and that -uses @scheme[equal?] to compare keys.} - - -@defproc[(make-hasheq) (and/c hash? hash-eq?)]{ - -Creates an empty mutable hash table that holds keys strongly and that -uses @scheme[eq?] to compare keys.} - - -@defproc[(make-weak-hash) (and/c hash? hash-weak?)]{ - -Creates an empty mutable hash table that holds keys weakly and that -uses @scheme[equal?] to compare keys.} - - -@defproc[(make-weak-hasheq) (and/c hash? hash-eq? hash-weak?)]{ - -Creates an empty mutable hash table that holds keys weakly and that -uses @scheme[eq?] to compare keys.} - - -@defproc[(make-immutable-hash [assocs (listof pair?)]) - (and/c hash? immutable?)]{ - -Creates an immutable hash table that compares keys with -@scheme[equal?]. In each element of @scheme[assocs], the @scheme[car] -of each pair is a key, and the @scheme[cdr] is the corresponding -value. The mappings are added to the table in the order that they -appear in @scheme[assocs], so later mappings can hide earlier -mappings.} - -@defproc[(make-immutable-hasheq [assocs (listof pair?)]) - (and/c hash? hash-eq? immutable?)]{ - -Like @scheme[make-immutable-hash], but the resulting hash table -compares keys with @scheme[eq?].} - - -@defproc[(hash-set! [hash (and/c hash? (not/c immutable?))] - [key any/c] - [v any/c]) void?]{ - -Maps @scheme[key] to @scheme[v] in @scheme[hash], overwriting -any existing mapping for @scheme[key].} - - -@defproc[(hash-set [hash (and/c hash? immutable?)] - [key any/c] - [v any/c]) - (and/c hash? immutable?)]{ - -Functionally extends @scheme[hash] by mapping @scheme[key] to -@scheme[v], overwriting any existing mapping for @scheme[key], and -returning an extended hash table.} - - -@defproc[(hash-ref [hash hash?] - [key any/c] - [failure-result any/c (lambda () (raise (make-exn:fail ....)))]) - any]{ - -Returns the value for @scheme[key] in @scheme[hash]. If no value -is found for @scheme[key], then @scheme[failure-result] determines the -result: - -@itemize{ - - @item{If @scheme[failure-result] is a procedure, it is called - (through a tail call) with no arguments to produce the result.} - - @item{Otherwise, @scheme[failure-result] is returned as the result.} - -}} - - -@defproc[(hash-remove! [hash (and/c hash? (not/c immutable?))] - [key any/c]) - void?]{ - -Removes any existing mapping for @scheme[key] in @scheme[hash].} - - -@defproc[(hash-remove [hash (and/c hash? immutable?)] - [key any/c]) - (and/c hash? immutable?)]{ - -Functionally removes any existing mapping for @scheme[key] in -@scheme[hash], returning the updated hash table.} - - -@defproc[(hash-map [hash hash?] - [proc (any/c any/c . -> . any/c)]) - (listof any/c)]{ - -Applies the procedure @scheme[proc] to each element in -@scheme[hash] in an unspecified order, accumulating the results -into a list. The procedure @scheme[proc] is called each time with a -key and its value. See the caveat above about concurrent -modification.} - - -@defproc[(hash-for-each [hash hash?] - [proc (any/c any/c . -> . any)]) - void?]{ - -Applies @scheme[proc] to each element in @scheme[hash] (for the -side-effects of @scheme[proc]) in an unspecified order. The procedure -@scheme[proc] is called each time with a key and its value. See the -caveat above about concurrent modification.} - - -@defproc[(hash-count [hash hash?]) - nonnegative-exact-integer?]{ - -Returns the number of keys mapped by @scheme[hash]. If -@scheme[hash] is not created with @scheme['weak], then the -result is computed in constant time and atomically. If -@scheme[hash] is created with @scheme['weak], see the caveat -above about concurrent modification.} - - -@defproc[(hash-iterate-first [hash hash?]) - (or/c false/c nonnegative-exact-integer?)]{ - -Returns @scheme[#f] if @scheme[hash] contains no elements, otherwise -it returns an integer that is a index to the first element in the hash -table; ``first'' refers to an unspecified ordering of the table -elements, and the index values are not necessarily consecutive -integers. For a mutable @scheme[hash], this index is guaranteed to -refer to the first item only as long as no items are added to or -removed from @scheme[hash].} - -@defproc[(hash-iterate-next [hash hash?] - [prev nonnegative-exact-integer?]) - (or/c false/c nonnegative-exact-integer?)]{ - -Returns either an integer that is an index to the element in -@scheme[hash] after the element indexed by @scheme[pos] (which is not -necessarily one more than @scheme[pos]) or @scheme[#f] if @scheme[pos] -refers to the last element in @scheme[hash]. If @scheme[pos] is not a -valid index, then the @exnraise[exn:fail:contract]. For a mutable -@scheme[hash], the result index is guaranteed to refer to its item -only as long as no items are added to or removed from @scheme[hash].} - - -@defproc[(hash-iterate-key [hash hash?] - [pos nonnegative-exact-integer?]) - any]{ - -Returns the key for the element in @scheme[hash] at index -@scheme[pos]. If @scheme[pos] is not a valid index for -@scheme[hash], the @exnraise[exn:fail:contract].} - - -@defproc[(hash-iterate-value [hash hash?] - [pos nonnegative-exact-integer?]) - any]{ - -Returns the value for the element in @scheme[hash] at index -@scheme[pos]. If @scheme[pos] is not a valid index for -@scheme[hash], the @exnraise[exn:fail:contract].} - - -@defproc[(hash-copy [hash hash?]) - (and/c hash? (not/c immutable?))]{ - -Returns a mutable hash table with the same mappings, same -key-comparison mode, and same key-holding strength as @scheme[hash].} - - -@defproc[(eq-hash-code [v any/c]) exact-integer?]{ - -Returns an exact integer; for any two @scheme[eq?] values, the -returned integer is the same. Furthermore, for the result integer -@scheme[k] and any other exact integer @scheme[j], @scheme[(= k j)] -implies @scheme[(eq? k j)].} - - -@defproc[(equal-hash-code [v any/c]) exact-integer?]{ - -Returns an exact integer; for any two @scheme[equal?] values, the -returned integer is the same. Furthermore, for the result integer -@scheme[k] and any other exact integer @scheme[j], @scheme[(= k j)] -implies @scheme[(eq? k j)]. A has code is computed even when -@scheme[v] contains a cycle through pairs, vectors, boxes, and/or -inspectable structure fields. See also @scheme[prop:equal+hash].} - -@defproc[(equal-secondary-hash-code [v any/c]) exact-integer?]{ - -Like @scheme[equal-hash-code], but computes a secondary value suitable -for use in double hashing.} +@include-section["hashes.scrbl"] @; ---------------------------------------------------------------------- @include-section["sequences.scrbl"] +@; ---------------------------------------------------------------------- +@include-section["dicts.scrbl"] + @; ---------------------------------------------------------------------- @include-section["procedures.scrbl"] diff --git a/collects/scribblings/reference/dicts.scrbl b/collects/scribblings/reference/dicts.scrbl new file mode 100644 index 0000000000..7681858a4c --- /dev/null +++ b/collects/scribblings/reference/dicts.scrbl @@ -0,0 +1,468 @@ +#lang scribble/doc +@(require "mz.ss" + scribble/eval) + +@(define dict-eval (make-base-eval)) +@interaction-eval[#:eval dict-eval (require scheme/dict)] + +@title[#:tag "dicts"]{Dictionaries} + +A @deftech{dictionary} is an instance of a datatype that maps keys to +values. The following datatypes are all dictionaries: + +@itemize[ + + @item{@techlink{hash tables};} + + @item{@techlink{vectors} (using only exact integers as keys);} + + @item{@techlink{lists} of @techlink{pairs} (an @deftech{association + list} using @scheme[equal?] to compare keys); and} + + @item{@techlink{structures} whose types have the @scheme[prop:dict] + property.} + +] + +@note-lib[scheme/dict] + +@defproc[(dict? [v any/c]) boolean?]{ + +Returns @scheme[#t] if @scheme[v] is a @tech{dictionary}, @scheme[#f] +otherwise. + +Beware that @scheme[dict?] is not a constant-time test on pairs, since +checking that @scheme[v] is an @tech{association list} may require +traversing the list. + +@examples[ +#:eval dict-eval +(dict? #hash((a . "apple"))) +(dict? '#("apple" "banana")) +(dict? '("apple" "banana")) +(dict? '((a . "apple") (b . "banana"))) +]} + + +@defproc[(dict-mutable? [d dict?]) boolean?]{ + +Returns @scheme[#t] if @scheme[d] is mutable via @scheme[dict-set!] +and maybe @scheme[dict-remove!], @scheme[#f] otherwise. + +@examples[ +#:eval dict-eval +(dict-mutable? #hash((a . "apple"))) +(dict-mutable? (make-hash)) +(dict-mutable? '#("apple" "banana")) +(dict-mutable? (vector "apple" "banana")) +(dict-mutable? '((a . "apple") (b . "banana"))) +]} + + + +@defproc[(dict-can-remove-keys? [d dict?]) boolean?]{ + +Returns @scheme[#t] if @scheme[d] supports removing mappings via +@scheme[dict-remove!] and/or @scheme[dict-remove], @scheme[#f] +otherwise. + +@examples[ +#:eval dict-eval +(dict-can-remove-keys? #hash((a . "apple"))) +(dict-can-remove-keys? '#("apple" "banana")) +(dict-can-remove-keys? '((a . "apple") (b . "banana"))) +]} + + +@defproc[(dict-can-functional-set? [d dict?]) boolean?]{ + +Returns @scheme[#t] if @scheme[d] supports functional update via +@scheme[dict-set] and maybe @scheme[dict-remove], @scheme[#f] +otherwise. + +@examples[ +#:eval dict-eval +(dict-can-functional-set? #hash((a . "apple"))) +(dict-can-functional-set? (make-hash)) +(dict-can-functional-set? '#("apple" "banana")) +(dict-can-functional-set? '((a . "apple") (b . "banana"))) +]} + + +@defproc[(dict-set! [dict (and/c dict? (not/c immutable?))] + [key any/c] + [v any/c]) void?]{ + +Maps @scheme[key] to @scheme[v] in @scheme[dict], overwriting any +existing mapping for @scheme[key]. The update can fail with a +@scheme[exn:fail:contract] exception if @scheme[dict] is not mutable +or if @scheme[key] is not an allowed key for the dictionary (e.g., not +an exact integer in the appropriate range when @scheme[dict] is a +@tech{vector}). + +@examples[ +#:eval dict-eval +(define h (make-hash)) +(dict-set! h 'a "apple") +h +(define v (vector #f #f #f)) +(dict-set! v 0 "apple") +v +]} + + +@defproc[(dict-set [dict (and/c dict? immutable?)] + [key any/c] + [v any/c]) + (and/c dict? immutable?)]{ + +Functionally extends @scheme[dict] by mapping @scheme[key] to +@scheme[v], overwriting any existing mapping for @scheme[key], and +returning an extended dictionary. The update can fail with a +@scheme[exn:fail:contract] exception if @scheme[dict] does not support +functional extension or if @scheme[key] is not an allowed key for the +dictionary. + +@examples[ +#:eval dict-eval +(dict-set #hash() 'a "apple") +(dict-set #hash((a . "apple") (b . "beer")) 'b "banana") +(dict-set '() 'a "apple") +(dict-set '((a . "apple") (b . "beer")) 'b "banana") +]} + + +@defproc[(dict-ref [dict dict?] + [key any/c] + [failure-result any/c (lambda () (raise (make-exn:fail ....)))]) + any]{ + +Returns the value for @scheme[key] in @scheme[dict]. If no value +is found for @scheme[key], then @scheme[failure-result] determines the +result: + +@itemize{ + + @item{If @scheme[failure-result] is a procedure, it is called + (through a tail call) with no arguments to produce the result.} + + @item{Otherwise, @scheme[failure-result] is returned as the result.} + +} + +@examples[ +#:eval dict-eval +(dict-ref #hash((a . "apple") (b . "beer")) 'a) +(dict-ref #hash((a . "apple") (b . "beer")) 'c) +(dict-ref #hash((a . "apple") (b . "beer")) 'c #f) +(dict-ref '((a . "apple") (b . "banana")) 'b) +(dict-ref #("apple" "banana") 1) +(dict-ref #("apple" "banana") 3 #f) +(dict-ref #("apple" "banana") -3 #f) +]} + + + +@defproc[(dict-remove! [dict (and/c dict? (not/c immutable?))] + [key any/c]) + void?]{ + +Removes any existing mapping for @scheme[key] in @scheme[dict]. The +update can fail if @scheme[dict] is not mutable or does not support +removing keys (as is the case for @tech{vectors}, for example). + +@examples[ +#:eval dict-eval +(define h (make-hash)) +(dict-set! h 'a "apple") +h +(dict-remove! h 'a) +h]} + + +@defproc[(dict-remove [dict (and/c dict? immutable?)] + [key any/c]) + (and/c dict? immutable?)]{ + +Functionally removes any existing mapping for @scheme[key] in +@scheme[dict], returning the updated dictionary. The update can fail +if @scheme[dict] does not support functional update or does not +support removing keys. + +@examples[ +#:eval dict-eval +(define h #hash()) +(define h (dict-set h 'a "apple")) +h +(dict-remove h 'a) +h +(dict-remove h 'z) +(dict-remove '((a . "apple") (b . "banana")) 'a) +]} + + +@defproc[(dict-map [dict dict?] + [proc (any/c any/c . -> . any/c)]) + (listof any/c)]{ + +Applies the procedure @scheme[proc] to each element in +@scheme[dict] in an unspecified order, accumulating the results +into a list. The procedure @scheme[proc] is called each time with a +key and its value. + +@examples[ +#:eval dict-eval +(dict-map #hash((a . "apple") (b . "banana")) vector) +]} + + +@defproc[(dict-for-each [dict dict?] + [proc (any/c any/c . -> . any)]) + void?]{ + +Applies @scheme[proc] to each element in @scheme[dict] (for the +side-effects of @scheme[proc]) in an unspecified order. The procedure +@scheme[proc] is called each time with a key and its value. + +@examples[ +#:eval dict-eval +(dict-for-each #hash((a . "apple") (b . "banana")) + (lambda (k v) + (printf "~a = ~s\n" k v))) +]} + + +@defproc[(dict-count [dict dict?]) + nonnegative-exact-integer?]{ + +Returns the number of keys mapped by @scheme[dict], usually in +constant time. + +@examples[ +#:eval dict-eval +(dict-count #hash((a . "apple") (b . "banana"))) +(dict-count #("apple" "banana")) +]} + + +@defproc[(dict-iterate-first [dict dict?]) any/c]{ + +Returns @scheme[#f] if @scheme[dict] contains no elements, otherwise +it returns a non-@scheme[#f] value that is a index to the first +element in the dict table; ``first'' refers to an unspecified ordering +of the dictionary elements. For a mutable @scheme[dict], this index is +guaranteed to refer to the first item only as long as no mappings are +added to or removed from @scheme[dict]. + +@examples[ +#:eval dict-eval +(dict-iterate-first #hash((a . "apple") (b . "banana"))) +(dict-iterate-first #hash()) +(dict-iterate-first #("apple" "banana")) +(dict-iterate-first '((a . "apple") (b . "banana"))) +]} + + +@defproc[(dict-iterate-next [dict dict?] + [pos any/c]) + any/c]{ + +Returns either a non-@scheme[#f] that is an index to the element in +@scheme[dict] after the element indexed by @scheme[pos] or @scheme[#f] +if @scheme[pos] refers to the last element in @scheme[dict]. If +@scheme[pos] is not a valid index, then the +@exnraise[exn:fail:contract]. For a mutable @scheme[dict], the result +index is guaranteed to refer to its item only as long as no items are +added to or removed from @scheme[dict]. The @scheme[dict-iterate-next] +operation should take constant time. + +@examples[ +#:eval dict-eval +(define h #hash((a . "apple") (b . "banana"))) +(define i (dict-iterate-first h)) +i +(dict-iterate-next h i) +(dict-iterate-next h (dict-iterate-next h i)) +]} + + +@defproc[(dict-iterate-key [dict dict?] + [pos any/c]) + any]{ + +Returns the key for the element in @scheme[dict] at index +@scheme[pos]. If @scheme[pos] is not a valid index for @scheme[dict], +the @exnraise[exn:fail:contract]. The @scheme[dict-iterate-key] +operation should take constant time. + +@examples[ +#:eval dict-eval +(define h '((a . "apple") (b . "banana"))) +(define i (dict-iterate-first h)) +(dict-iterate-key h i) +(dict-iterate-key h (dict-iterate-next h i)) +]} + + + +@defproc[(dict-iterate-value [dict dict?] + [pos any/c]) + any]{ + +Returns the value for the element in @scheme[dict] at index +@scheme[pos]. If @scheme[pos] is not a valid index for @scheme[dict], +the @exnraise[exn:fail:contract]. The @scheme[dict-iterate-key] +operation should take constant time. + +@examples[ +#:eval dict-eval +(define h '((a . "apple") (b . "banana"))) +(define i (dict-iterate-first h)) +(dict-iterate-value h i) +(dict-iterate-value h (dict-iterate-next h i)) +]} + + +@defproc[(in-dict [dict dict?]) sequence?]{ Returns a @tech{sequence} +whose each element is two values: a key and corresponding value from +@scheme[dict]. + +@examples[ +#:eval dict-eval +(define h #hash((a . "apple") (b . "banana"))) +(for/list ([(k v) (in-dict h)]) + (format "~a = ~s" k v)) +]} + + +@defproc[(in-dict-keys [dict dict?]) sequence?]{ +Returns a sequence whose elements are the keys of @scheme[dict]. + +@examples[ +#:eval dict-eval +(define h #hash((a . "apple") (b . "banana"))) +(for/list ([k (in-dict-keys h)]) + k) +]} + +@defproc[(in-dict-values [dict dict?]) sequence?]{ +Returns a sequence whose elements are the values of @scheme[dict]. + +@examples[ +#:eval dict-eval +(define h #hash((a . "apple") (b . "banana"))) +(for/list ([v (in-dict-values h)]) + v) +]} + +@defproc[(in-dict-pairs [dict dict?]) sequence?]{ Returns a sequence +whose elements are pairs, each containing a key and its value from +@scheme[dict] (as opposed to using @scheme[in-dict], which gets the +key and value as separate values for each element). + +@examples[ +#:eval dict-eval +(define h #hash((a . "apple") (b . "banana"))) +(for/list ([p (in-dict-pairs h)]) + p) +]} + + + +@defthing[prop:dict struct-type-property?]{ + +A @tech{structure type property} (see @secref["structprops"]) that +supplies dictionary-operation implementations for a structure +type. The property value must be a vector of ten procedures (some +optional) that are applied only to instances of the structure type +that has the property: + +@itemize[ + + @item{@scheme[_ref] : a procedure like @scheme[dict-ref] that accepts + either two or three arguments} + + @item{@scheme[_set!] : a procedure like @scheme[dict-set!] that accepts + three arguments, or @scheme[#f] if mutation is not supported} + + @item{@scheme[_set] : a procedure like @scheme[dict-set] that accepts + three arguments and returns an updated dictionary, or + @scheme[#f] if functional update is not supported} + + @item{@scheme[_remove!] : a procedure like @scheme[dict-remove!] that + accepts two arguments, or @scheme[#f] if mutation is not + supported or if key removal is not supported} + + @item{@scheme[_remove] : a procedure like @scheme[dict-remove] that + accepts two arguments and returns an updated dictionary, or + @scheme[#f] if functional update or key removal is not + supported} + + @item{@scheme[_count] : a procedure like @scheme[dict-count] that accepts + one argument} + + @item{@scheme[_iterate-first] : a procedure like + @scheme[dict-iterate-first] that accepts one argument} + + @item{@scheme[_iterate-next] : a procedure like + @scheme[dict-iterate-next] that accepts two arguments; the + procedure is responsible for checking that the second argument + is a valid position for the first argument} + + @item{@scheme[_iterate-key] : a procedure like + @scheme[dict-iterate-key] that accepts two arguments; the + procedure is responsible for checking that the second argument + is a valid position for the first argument} + + @item{@scheme[_iterate-value] : a procedure like + @scheme[dict-iterate-value] that accepts two arguments; the + procedure is responsible for checking that the second argument + is a valid position for the first argument} + +]} + +@deftogether[( +@defproc[(make-custom-hash [eql? (any/c any/c . -> . any/c)] + [hash-proc (any/c . -> . exact-integer?)] + [hash2-proc (any/c . -> . exact-integer?)]) + dict?] +@defproc[(make-immutable-custom-hash [eql? (any/c any/c . -> . any/c)] + [hash-proc (any/c . -> . exact-integer?)] + [hash2-proc (any/c . -> . exact-integer?)]) + dict?] +@defproc[(make-weak-custom-hash [eql? (any/c any/c . -> . any/c)] + [hash-proc (any/c . -> . exact-integer?)] + [hash2-proc (any/c . -> . exact-integer?)]) + dict?] +)]{ + +Creates a dictionary that is implemented in terms of a hash table +where keys are compared with @scheme[eql?] and hashed with +@scheme[hash-proc] and @scheme[hash2-proc]. See +@scheme[prop:equal+hash] for information on suitable equality and +hashing functions. + +The @scheme[make-custom-hash] and @scheme[make-weak-custom-hash] +functions create a mutable dictionary that does not support functional +update, while @scheme[make-immutable-custom-hash] creates an immutable +dictionary that supports functional update. The dictionary created by +@scheme[make-weak-custom-hash] retains its keys weakly, like the result +of @scheme[make-weak-hash]. + +Dictionaries created by @scheme[make-custom-hash] and company are +@scheme[equal?] when they have the same mutability and key strength, +the associated procedures are @scheme[equal?], and the key--value +mappings are the same when keys and values are compared with +@scheme[equal?]. + +@examples[ +#:eval dict-eval +(define h (make-custom-hash (lambda (a b) + (string=? (format "~a" a) + (format "~a" b))) + (lambda (a) + (equal-hash-code + (format "~a" a))))) +(dict-set! h 1 'one) +(dict-ref h "1") +]} diff --git a/collects/scribblings/reference/eval-model.scrbl b/collects/scribblings/reference/eval-model.scrbl new file mode 100644 index 0000000000..41722d9f68 --- /dev/null +++ b/collects/scribblings/reference/eval-model.scrbl @@ -0,0 +1,772 @@ +#lang scribble/doc +@(require scribble/struct + (for-syntax scheme/base) + "mz.ss" + "prog-steps.ss") + +@(define reduces (make-element #f (list 'rarr))) +@(define rspace (make-element "ghost" (list 'rarr))) + +@(define *redex (lambda (c) + (make-element "highlighted" (list c)))) +@(define-syntax redex + (syntax-rules () [(_ a) (*redex (scheme a))])) + + +@(define hole (make-element #f (list "[]"))) +@(define (*sub c e) (make-element #f (list c "[" e "]"))) +@(define langle (make-element 'tt (list "<"))) +@(define rangle (make-element 'tt (list ">"))) +@(define comma (make-element 'tt (list ", "))) +@(define-syntax sub + (syntax-rules () [(_ a b) (*sub (scheme a) (scheme b))])) +@(define (*state c e) (make-element #f (list langle c comma e rangle))) +@(define-syntax state + (syntax-rules () [(_ a b) (*state (scheme a) (scheme b))])) +@(define (frame n) + (make-element "schemevariable" + (list "C" (make-element 'subscript (list (format "~a" n)))))) + +@;------------------------------------------------------------------------ +@title[#:tag "eval-model"]{Evaluation Model} + +Scheme evaluation can be viewed as the simplification of expressions +to obtain values. For example, just as an elementary-school student +simplifies + +@verbatim{ 1 + 1 = 2} + +Scheme evaluation simplifies + +@schemeblock[ +(+ 1 1) #, @reduces 2 +] + +The arrow @reduces above replaces the more traditional @tt{=} to +emphasize that evaluation proceeds in a particular direction towards +simpler expressions. In particular, a @deftech{value} is an +expression that evaluation simplifies no further, such as the number +@scheme[2]. + +@;------------------------------------------------------------------------ +@section[#:tag "cont-model"]{Sub-expression Evaluation and Continuations} + +Some simplifications require more than one step. For example: + +@schemeblock[ +(- 4 #,(redex (+ 1 1))) #,reduces #,(redex (- 4 2)) #,reduces 2 +] + +An expression that is not a @tech{value} can always be partitioned +into two parts: a @deftech{redex}, which is the part that changed in a +single-step simplification (show in blue), and the +@deftech{continuation}, which is the surrounding expression +context. In @scheme[(- 4 (+ 1 1))], the redex is @scheme[(+ 1 1)], and +the continuation is @scheme[(- 4 #, @hole)], where @hole takes the +place of the redex. That is, the continuation says how to ``continue'' +after the @tech{redex} is reduced to a @tech{value}. + +Before some things can be evaluated, some sub-expressions must be +evaluated; for example, in the application @scheme[(- 4 (+ 1 1))], the +application of @scheme[-] cannot be reduced until the sub-expression +@scheme[(+ 1 1)] is reduced. + +Thus, the specification of each syntactic form specifies how (some of) +its sub-expressions are evaluated, and then how the results are +combined to reduce the form away. + +The @deftech{dynamic extent} of an expression is the sequence of +evaluation steps during which an expression contains the @tech{redex}. + +@;------------------------------------------------------------------------ +@section{Tail Position} + +An expression @scheme[_expr1] is in @deftech{tail position} with +respect to an enclosing expression @scheme[_expr2] if, whenever +@scheme[_expr1] becomes a redex, its @tech{continuation} is the same +as was the enclosing @scheme[_expr2]'s @tech{continuation}. + +For example, the @scheme[(+ 1 1)] expression is @italic{not} in @tech{tail +position} with respect to @scheme[(- 4 (+ 1 1))]. To illustrate, we use +the notation @sub[_C _expr] to mean the expression that is produced by +substituting @scheme[_expr] in place of @hole in the @tech{continuation} +@scheme[_C]: + +@schemeblock[ +#, @sub[_C (- 4 (+ 1 1))] #, @reduces #, @sub[_C (- 4 2)] +] + +In this case, the @tech{continuation} for reducing @scheme[(+ 1 1)] is +@sub[_C (+ 4 #, @hole)], not just @scheme[_C]. + +In contrast, @scheme[(+ 1 1)] is in @tech{tail position} with respect +to @scheme[(if (zero? 0) (+ 1 1) 3)], because, for any continuation +@scheme[_C], + +@schemeblock[ +#, @sub[_C (if (zero? 0) (+ 1 1) 3)] #, @reduces #, @sub[_C (if #t (+ 1 1) 3)] #, @reduces #, @sub[_C (+ 1 1)] +] + +The steps in this reduction sequence are driven by the definition of +@scheme[if], and they do not depend on the @tech{continuation} +@scheme[_C]. The ``then'' branch of an @scheme[if] form is always in +@tech{tail position} with respect to the @scheme[if] form. Due to a +similar reduction rule for @scheme[if] and @scheme[#f], the ``else'' +branch of an @scheme[if] form is also in @tech{tail position}. + +@tech{Tail-position} specifications provide a guarantee about the +asymptotic space consumption of a computation. In general, the +specification of @tech{tail positions} goes with each syntactic form, +like @scheme[if]. + +@;------------------------------------------------------------------------ +@section[#:tag "values-model"]{Multiple Return Values} + +A Scheme expression can evaluate to @deftech{multiple values}, in the +same way that a procedure can accept multiple arguments. + +Most @tech{continuations} expect a particular number of result +@tech{values}. Indeed, most @tech{continuations}, such as @scheme[(+ +#, @hole 1)] expect a single @tech{value}. The @tech{continuation} +@scheme[(let-values ([(x y) #, @hole]) _expr)] expects two result +@tech{values}; the first result replaces @scheme[x] in the body +@scheme[_expr], and the second replaces @scheme[y] in +@scheme[_expr]. The @tech{continuation} @scheme[(begin #, @hole (+ 1 +2))] accepts any number of result @tech{values}, because it ignores +the result(s). + +In general, the specification of a syntactic form inidicates the +number of @tech{values} that it produces and the number that it +expects from each of its sub-expression. In addtion, some procedures +(notably @scheme[values]) produce multiple @tech{values}, and some +procedures (notably @scheme[call-with-values]) create continuations +internally that accept a certain number of @tech{values}. + +@;------------------------------------------------------------------------ +@section{Top-Level Variables} + +Given + +@verbatim{ x = 10} + +then an algebra student simplifies @tt{x + 1} as follows: + +@verbatim{ x + 1 = 10 + 1 = 11} + +Scheme works much the same way, in that a set of @tech{top-level +variables} are available for substitutions on demand during +evaluation. For example, given + +@schemeblock[ +(define x 10) +] + +then + +@schemeblock[ +#,(redex (+ x 1)) #,reduces #,(redex (+ 10 1)) #,reduces 11 +] + +In Scheme, the way definitions appear is just as important as the way +that they are used. Scheme evaluation thus keeps track of both +definitions and the current expression, and it extends the set of +definitions in response to evaluating forms such as @scheme[define]. + +Each evaluation step, then, takes the current set of definitions and +program to a new set of definitions and program. Before a +@scheme[define] can be moved into the set of definitions, its +right-hand expression must be reduced to a @tech{value}. + +@prog-steps/no-obj[ +[{} + (begin (define x (code:hilite (+ 9 1))) (+ x 1))] +[{} + (begin (code:hilite (define x 10)) (+ x 1))] +[{(define x 10)} + (code:hilite (begin (void) (+ x 1)))] +[{(define x 10)} + (+ (code:hilite x) 1)] +[{(define x 10)} + (code:hilite (+ 10 1))] +[{(define x 10)} + 11] +] + +Using @scheme[set!], a program can change the value associated with an +existing @tech{top-level variable}: + +@prog-steps/no-obj[ +[{(define x 10)} + (begin (code:hilite (set! x 8)) x)] +[{(define x 8)} + (code:hilite (begin (void) x))] +[{(define x 8)} + (code:hilite x)] +[{(define x 8)} + 8] +] + +@;------------------------------------------------------------------------ +@section{Objects and Imperative Update} + +In addition to @scheme[set!] for imperative update of @tech{top-level +variables}, various procedures enable the modification of elements +within a compound data structure. For example, @scheme[vector-set!] +modifies the content of a vector. + +To allow such modifications to data, we must distingiush between +@tech{values}, which are the results of expressions, and +@deftech{objects}, which hold the data referenced by a value. + +A few kinds of @tech{objects} can serve directly as values, including +booleans, @scheme[(void)], and small exact integers. More generally, +however, a @tech{value} is a reference to an @tech{object}. For +example, a @tech{value} can be a reference to a particular vector that +currently holds the value @scheme[10] in its first slot. If an +@tech{object} is modified, then the modification is visible through +all copies of the @tech{value} that reference the same @tech{object}. + +In the evaluation model, a set of @tech{objects} must be carried along +with each step in evaluation, just like the definition set. Operations +that create @tech{objects}, such as @scheme[vector], add to the set of +@tech{objects}: + +@prog-steps[ +[{} + {} + (begin (define x (code:hilite (vector 10 20))) + (define y x) + (vector-set! x 0 11) + (vector-ref y 0))] +[{(define (vector 10 20))} + {} + (begin (code:hilite (define x )) + (define y x) + (vector-set! x 0 11) + (vector-ref y 0))] +[{(define (vector 10 20))} + {(define x )} + (code:hilite (begin (void) + (define y x) + (vector-set! x 0 11) + (vector-ref y 0)))] +[{(define (vector 10 20))} + {(define x )} + (begin (define y (code:hilite x)) + (vector-set! x 0 11) + (vector-ref y 0))] +[{(define (vector 10 20))} + {(define x )} + (begin (code:hilite (define y )) + (vector-set! x 0 11) + (vector-ref y 0))] +[{(define (vector 10 20))} + {(define x ) + (define y )} + (code:hilite (begin (void) + (vector-set! x 0 11) + (vector-ref y 0)))] +[{(define (vector 10 20))} + {(define x ) + (define y )} + (begin (vector-set! (code:hilite x) 0 11) + (vector-ref y 0))] +[{(define (vector 10 20))} + {(define x ) + (define y )} + (begin (code:hilite (vector-set! 0 11)) + (vector-ref y 0))] +[{(define (vector 11 20))} + {(define x ) + (define y )} + (code:hilite (begin (void) + (vector-ref y 0)))] +[{(define (vector 11 20))} + {(define x ) + (define y )} + (vector-ref (code:hilite y) 0)] +[{(define (vector 11 20))} + {(define x ) + (define y )} + (code:hilite (vector-ref 0))] +[{(define (vector 11 20))} + {(define x ) + (define y )} + 11] +] + +The distinction between a @tech{top-level variable} and an object +reference is crucial. A @tech{top-level variable} is not a +@tech{value}; each time a @tech{variable} expression is evaluated, the +value is extracted from the current set of definitions. An object +reference, in contrast is a value, and therefore needs no further +evaluation. The model evaluation steps above use angle-bracketed +@scheme[] for an object reference to distinguish it from a +@tech{variable} name. + +A direct object reference can never appear in a text-based source +program. A program representation created with +@scheme[datum->syntax-object], however, can embed direct references to +existing @tech{objects}. + +@;------------------------------------------------------------------------ +@section[#:tag "model-eq"]{Object Identity and Comparisons} + +The @scheme[eq?] operator compares two @tech{values}, returning +@scheme[#t] when the values refer to the same @tech{object}. This form +of equality is suitable for comparing objects that support imperative +update (e.g., to determine that the effect of modifying an object +through one reference is visible through another reference). Also, an +@scheme[eq?] test evaluates quickly, and @scheme[eq?]-based hashing +is more lightweight than @scheme[equal?]-based hashing in hash tables. + +In some cases, however, @scheme[eq?] is unsuitable as a comparison +operator, because the generation of @tech{objects} is not clearly +defined. In particular, two applications of @scheme[+] to the same two +exact integers may or may not produce results that are @scheme[eq?], +although the results are always @scheme[equal?]. Similarly, evaluation +of a @scheme[lambda] form typically generates a new procedure +@tech{object}, but it may re-use a procedure @tech{object} previously +generated by the same source @scheme[lambda] form. + +The behavior of a datatype with respect to @scheme[eq?] is generally +specified with the datatype and its associated procedures. + +@;------------------------------------------------------------------------ +@section[#:tag "gc-model"]{Garbage Collection} + +In the program state + +@prog-steps[ +[{(define (vector 10 20)) + (define (vector 0))} + {(define x )} + (+ 1 x)] +] + +evaluation cannot depend on @scheme[], because it is not part of +the program to evaluate, and it is not referenced by any definition +that is accessible in the program. The @tech{object} @scheme[] may +therefore be removed from the evaluation by @deftech{garbage +collection}. + +A few special compound datatypes hold @deftech{weak references} to +objects. Such weak references are treated specially by the garbage +collector in determining which @tech{objects} are reachable for the +remainder of the computation. If an @tech{object} is reachable only +via a @tech{weak reference}, then the object can be reclaimed, and the +@tech{weak reference} is replaced by a different @tech{value} +(typically @scheme[#f]). + +@;------------------------------------------------------------------------ +@section{Procedure Applications and Local Variables} + +Given + +@verbatim{ f(x) = x + 10} + +then an algebra student simplifies @tt{f(1)} as follows: + +@verbatim{ f(7) = 7 + 10 = 17} + +The key step in this simplification is take the body of the defined +function @tt{f}, and then replace each @tt{x} with the actual +@tech{value} @tt{1}. + +Scheme procedure application works much the same way. A procedure is +an @tech{object}, so evaluating @scheme[(f 7)] starts with a +@tech{variable} lookup: + +@prog-steps[ +[{(define (lambda (x) (+ x 10)))} + {(define f )} + ((code:hilite f) 7)] +[{(define (lambda (x) (+ x 10)))} + {(define f )} + (code:hilite ( 7))] +] + +Unlike in algebra, however, the @tech{value} associated with an +argument can be changed in the body of a procedure by using +@scheme[set!], as in the example @scheme[(lambda (x) (begin (set! x 3) +x))]. Since the @tech{value} associated with @scheme[x] can be +changed, an actual value for cannot be substituted for @scheme[x] when +the procedure is applied. + +Instead, a new @deftech{location} is created for each @tech{variable} +on each application. The argument @tech{value} is placed in the +@tech{location}, and each instance of the @tech{variable} in the +procedure body is replaced with the new @tech{location}: + +@prog-steps[ +[{(define (lambda (x) (+ x 10)))} + {(define f )} + (code:hilite ( 7))] +[{(define (lambda (x) (+ x 10)))} + {(define f ) + (define xloc 7)} + (+ (code:hilite xloc) 10)] +[{(define (lambda (x) (+ x 10)))} + {(define f ) + (define xloc 7)} + (code:hilite (+ 7 10))] +[{(define (lambda (x) (+ x 10)))} + {(define f ) + (define xloc 7)} + 17] +] + +A @tech{location} is the same as a @tech{top-level variable}, but when +a @tech{location} is generated, it (conceptually) uses a name that has +not been used before and that cannot not be generated again or +accessed directly. + +Generating a @tech{location} in this way means that @scheme[set!] +evaluates for @tech{local variables} in the same way as for +@tech{top-level variables}, because the @tech{local variable} is +always replaced with a @tech{location} by the time the @scheme[set!] +form is evaluated: + +@prog-steps[ +[{(define (lambda (x) (begin (set! x 3) x)))} + {(define f )} + ((code:hilite f) 7)] +[{(define (lambda (x) (begin (set! x 3) x)))} + {(define f )} + (code:hilite ( 7))] +[{(define (lambda (x) (begin (set! x 3) x)))} + {(define f ) + (define xloc 7)} + (begin (code:hilite (set! xloc 3)) xloc)] +[{(define (lambda (x) (begin (set! x 3) x)))} + {(define f ) + (define xloc 3)} + (code:hilite (begin (void) xloc))] +[{(define (lambda (x) (begin (set! x 3) x)))} + {(define f ) + (define xloc 3)} + (code:hilite xloc)] +[{(define (lambda (x) (begin (set! x 3) x)))} + {(define f ) + (define xloc 3)} + 3] +] + +The substition and @tech{location}-generation step of procedure +application requires that the argument is a @tech{value}. Therefore, +in @scheme[((lambda (x) (+ x 10)) (+ 1 2))], the @scheme[(+ 1 2)] +sub-expression must be simplified to the @tech{value} @scheme[3], and +then @scheme[3] can be placed into a @tech{location} for +@scheme[x]. In other words, Scheme is a @deftech{call-by-value} +language. + +Evaluation of a local-variable form, such as @scheme[(let ([x (+ 1 +2)]) _expr)], is the same as for a procedure call. After @scheme[(+ 1 +2)] produces a @tech{value}, it is stored in a fresh @tech{location} +that replaces every instance of @scheme[x] in @scheme[_expr]. + +@;------------------------------------------------------------------------ +@section{Variables and Locations} + +A @deftech{variable} is a placeholder for a @tech{value}, and an +expressions in an initial program refer to @tech{variables}. A +@deftech{top-level variable} is both a @tech{variable} and a +@tech{location}. Any other @tech{variable} is always replaced by a +@tech{location} at run-time, so that evaluation of expressions +involves only @tech{locations}. A single @deftech{local variable} +(i.e., a non-top-level, non-module-level @tech{variable}), such as a +procedure argument, can correspond to different @tech{locations} +through different instantiations. + +For example, in the program + +@schemeblock[(define y (+ (let ([x 5]) x) 6))] + +both @scheme[y] and @scheme[x] are @tech{variables}. The @scheme[y] +@tech{variable} is a @tech{top-level variable}, and the @scheme[x] is +a @tech{local variable}. When this code is evaluated, a +@tech{location} is created for @scheme[x] to hold the value +@scheme[5], and a @tech{location} is also created for @scheme[y] to +hold the value @scheme[6]. + +The replacement of a @tech{variable} with a @tech{location} during +evaluation implements Scheme's @deftech{lexical scoping}. For example, +when a procedure-argument @tech{variable} @scheme[x] is replaced by +the @tech{location} @scheme[xloc], then it is replaced throughout the +body of the procedure, including with any nested @scheme[lambda] +forms. As a result, future references of the @tech{variable} always +access the same @tech{location}. + +@;------------------------------------------------------------------------ +@section[#:tag "module-eval-model"]{Modules and Module-Level Variables} + +Most definitions in PLT Scheme are in modules. In terms of evaluation, +a module is essentially a prefix on a defined name, so that different +modules can define the name. That is, a @deftech{module-level +variable} is like a @tech{top-level variable} from the perspective of +evaluation. + +One difference between a module an a top-level definition is that a +module can be declared without instantiating its module-level +definitions. Evaluation of a @scheme[require] @deftech{instantiates} +(i.e., triggers the @deftech{instantiation} of) a declared module, +which creates variables that correspond to its module-level +definitions. + +For example, given the module declaration + +@schemeblock[ +(module m mzscheme + (define x 10)) +] + +the evaluation of @scheme[(require m)] creates the variable @scheme[x] +and installs @scheme[10] as its value. This @scheme[x] is unrelated to +any top-level definition of @scheme[x]. + +@;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@subsection[#:tag "module-phase"]{Module Phases} + +A module can be @tech{instantiate}d in multiple @deftech{phases}. A +phase is an integer that, again, is effectively a prefix on the names +of module-level definitions. A top-level @scheme[require] +@tech{instantiates} a module at @tech{phase} 0, if the module is not +already @tech{instantiate}d at phase 0. A top-level +@scheme[require-for-syntax] @tech{instantiates} a module at +@tech{phase} 1 (if it is not already @tech{instantiate}d at that +level); a @scheme[require-for-syntax] also has a different binding +effect on further program parsing, as described in +@secref["intro-binding"]. + +Within a module, some definitions are shifted by a phase already; the +@scheme[define-for-syntax] form is like @scheme[define], but it +defines a variable at relative @tech{phase} 1, instead of relative +@tech{phase} 0. Thus, if the module is @tech{instantiate}d at phase 1, +the variables for @scheme[define-for-syntax] are created at phase 2, +and so on. Moreover, this relative phase acts as another layer of +prefixing, so that a @scheme[define] of @scheme[x] and a +@scheme[define-for-syntax] of @scheme[x] can co-exist in a module +without colliding. Again, the higher phases are mainly related to +program parsing, instead of normal evaluation. + +If a module @tech{instantiate}d at @tech{phase} @math{n} +@scheme[require]s another module, then the @scheme[require]d module is +first @tech{instantiate}d at phase @math{n}, and so on +transitively. (Module @scheme[require]s cannot form cycles.) If a +module @tech{instantiate}d at phase @math{n} +@scheme[require-for-syntax]es another module, the other module is +first @tech{instantiate}d at @tech{phase} @math{n+1}, and so on. If a +module @tech{instantiate}d at phase @math{n} for non-zero @math{n} +@scheme[require-for-template]s another module, the other module is +first @tech{instantiate}d at @tech{phase} @math{n-1}, and so on. + +A final distinction among module @tech{instantiations} is that +multiple @tech{instantiations} may exist at phase 1 and higher. These +@tech{instantiations} are created by the parsing of module forms (see +@secref["mod-parse"]), and are, again, conceptually distinguished +by prefixes. + +@;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +@subsection[#:tag "module-redeclare"]{Module Re-declarations} + +@section-index["modules" "re-define"] + +When a module is declared using a name for which a module is already +declared, the new declaration's definitions replace and extend the old +declarations. If a variable in the old declaration has no counterpart +in the new declaration, the old variable continues to exist, but its +binding is not included in the @tech{lexical information} for the +module body. If a new variable definition has a counterpart in the old +declaration, it effectively assigns to the old variable. + +If a module is @tech{instantiate}d in any @tech{phase}s before it is +re-declared, each re-declaration of the module is immediately +@tech{instantiate}d in the same @tech{phase}s. + +@;------------------------------------------------------------------------ +@section[#:tag "mark-model"]{Continuation Frames and Marks} + +Every continuation @scheme[_C] can be partitioned into +@deftech{continuation frames} @frame[1], @frame[2], ..., @frame["n"] +such that @scheme[_C] = @*sub[@frame[1] @*sub[@frame[2] @*sub["..." +@frame["n"]]]], and no frame @frame["i"] can be itself partitioned +into smaller continuations. Evaluation steps add and remove frames to +the current continuation, typically one at a time. + +Each frame is conceptually annotated with a set of +@deftech{continuation marks}. A mark consists of a key and its value; +the key is an arbitrary value, and each frame includes at most one +mark for any key. Various operations set and extract marks from +continuations, so that marks can be used to attach information to a +dynamic extent. For example, marks can be used to record information +for a ``stack trace'' to be used when an exception is raised, or +to implement dynamic scope. + +@;------------------------------------------------------------------------ +@section[#:tag "prompt-model"]{Prompts, Delimited Continuations, and Barriers} + +A @deftech{prompt} is a special kind of continuation frame that is +annotated with a specific @deftech{prompt-tag} (essentially a +continuation mark). Various operations allow the capture of frames in +the continuation from the redex position out to the nearest enclosing +prompt with a particular prompt tag; such a continuation is sometimes +called a @deftech{delimited continuation}. Other operations allow the +current continuation to be extended with a captured continuation +(specifically, a @deftech{composable continuation}). Yet other +operations abort the computation to the nearest enclosing prompt with +a particular tag, or replace the continuation to the nearest enclosing +prompt with another one. When a delimited continuation is captured, +the marks associated with the relevant frames are also captured. + +A @deftech{continuation barrier} is another kind of continuation frame +that prohibits certain replacements of the current continuation with +another. Specifically, while an abort is allowed to remove a portion +of the continuation containing a prompt, the continuation can be +replaced by another only when the replacement also includes the +continuation barrier. Certain operations install barriers +automatically; in particular, when an exception handler is called, a +continuation barrier prohibits the continuation of the handler from +capturing the continuation past the exception point. + +A @deftech{escape continuation} is essentially a derived concept. It +combines a prompt for escape purposes with a continuation for +mark-gathering purposes. as the name implies, escape continuations are +used only to abort to the point of capture, which means that +escape-continuation aborts can cross continuation barriers. + +@;------------------------------------------------------------------------ +@section[#:tag "thread-model"]{Threads} + +Scheme supports multiple, pre-emptive @deftech{threads} of +evaluation. In terms of the evaluation model, this means that each +step in evaluation actually consists of multiple concurrent +expressions, rather than a single expression. The expressions all +share the same objects and top-level variables, so that they can +communicate through shared state. Most evaluation steps involve a +single step in a single expression, but certain synchronization +primitives require multiple threads to progress together in one step. + +In addition to the state that is shared among all threads, each thread +has its own private state that is accessed through @deftech{thread +cells}. A thread cell is similar to a normal mutable object, but a +change to the value inside a thread cell is seen only when extracting +a value from the cell from the same thread. A thread cell can be +@deftech{preserved}; when a new thread is created, the creating +thread's value for a preserved thread cell serves as the initial value +for the cell in the created thread. For a non-preserved thread cell, a +new thread sees the same initial value (specified when the thread cell +is created) as all other threads. + +@;------------------------------------------------------------------------ +@section[#:tag "parameter-model"]{Parameters} + +@deftech{Parameters} are essentially a derived concept in Scheme; they +are defined in terms of continuation marks and thread cells. However, +parameters are also built in, in the sense that some primitive +procedures consult parameter values. For example, the default output +stream for primitive output operations is determined by a parameter. + +A parameter is a setting that is both thread-specific and +continuation-specific. In the empty continuation, each parameter +corresponds to a preserved thread cell; a corresponding +@deftech{parameter procedure} accesses and sets the thread cell's +value for the current thread. + +In a non-empty continuation, a parameter's value is determined through +a @deftech{parameterization} that is associated with the nearest +enclosing continuation frame though a continuation mark (whose key is +not directly accessible). A parameterization maps each parameter to a +preserved thread cell, and the combination of thread cell and current +thread yields the parameter's value. A parameter procedure sets or +accesses the relevant thread cell for its parameter. + +Various operations, such as @scheme[parameterize] or +@scheme[with-parameterization], install a parameterization into the +current continuation's frame. + +@;------------------------------------------------------------------------ +@section[#:tag "exn-model"]{Exceptions} + +@deftech{Exceptions} are essentially a derived concept in Scheme; they +are defined in terms of continuations, prompts, and continuation +marks. However, exceptions are also built in, in the sense that +primitive forms and procedures may raise exceptions. + +A handler for uncaught exceptions is designated through a built-in +parameter. A handler to catch exceptions can be associated with a +continuation frame though a continuation mark (whose key is not +directly accessible). When an exception is raised, the current +continuation's marks determine a chain of handler procedures that are +consulted to handle the exception. + +One potential action of an exception handler is to abort the current +continuation up to an enclosing prompt with a particular tag. The +default handler for uncaught exceptions, in particular, aborts to a +particular tag for which a prompt is always present, because the +prompt is installed in the outermost frame of the continuation for any +new thread. + +@;------------------------------------------------------------------------ +@section[#:tag "custodian-model"]{Custodians} + +A @deftech{custodian} manages a collection of threads, file-stream +ports, TCP ports, TCP listeners, UDP sockets, and byte converters. +Whenever a thread, file-stream port, TCP port, TCP listener, or UDP +socket is created, it is placed under the management of the +@deftech{current custodian} as determined by the +@scheme[current-custodian] @tech{parameter}. + +@margin-note{In MrEd, custodians also manage eventspaces.} + +Except for the root custodian, every @tech{custodian} itself it +managed by a @tech{custodian}, so that custodians form a hierarchy. +Every object managed by a subordinate custodian is also managed by the +custodian's owner. + +When a @tech{custodian} is shut down via +@scheme[custodian-shutdown-all], it forcibly and immediately closes +the ports, TCP connections, etc. that it manages, as well as +terminating (or suspending) its threads. A custodian that has been +shut down cannot manage new objects. If the current custodian is shut +down before a procedure is called to create a managed resource (e.g., +@scheme[open-input-port], @scheme[thread]), the +@exnraise[exn:fail:contract]. + +A thread can have multiple managing custodians, and a suspended thread +created with @scheme[thread/suspend-to-kill] can have zero +custodians. Extra custodians become associated with a thread through +@scheme[thread-resume] (see @secref["threadkill"]). When a thread +has multiple custodians, it is not necessarily killed by a +@scheme[custodian-shutdown-all], but shut-down custodians are removed +from the thread's managing set, and the thread is killed when its +managing set becomes empty. + +The values managed by a custodian are only weakly held by the +custodian. As a result, a @tech{will} can be executed for a value that +is managed by a custodian. In addition, a custodian only weakly +references its subordinate custodians; if a subordinate custodian is +unreferenced but has its own subordinates, then the custodian may be +collected, at which point its subordinates become immediately +subordinate to the collected custodian's superordinate custodian. + +In addition to the other entities managed by a custodian, a +@defterm{custodian box} created with @scheme[make-custodian-box] +strongly holds onto a value placed in the box until the box's +custodian is shut down. The custodian only weakly retains the box +itself, however (so the box and its content can be collected if there +are no other references to them). + +When MzScheme is compiled with support for per-custodian memory +accounting (see @scheme[custodian-memory-accounting-available?]), the +@scheme[current-memory-use] procedure can report a custodian-specific +result. This result determines how much memory is occupied by objects +that are reachable from the custodian's managed values, especially its +threads, and including its sub-custodians' managed values. If an +object is reachable from two custodians where neither is an ancestor +of the other, an object is arbitrarily charged to one of the other, +and the choice can change after each collection; objects reachable +from both a custodian and its descendant, however, are reliably +charged to the descendant. Reachability for per-custodian accounting +does not include weak references, references to threads managed by +non-descendant custodians, references to non-descendant custodians, or +references to custodian boxes for non-descendant custodians. diff --git a/collects/scribblings/reference/for.scrbl b/collects/scribblings/reference/for.scrbl index f86474d28b..978576ccb4 100644 --- a/collects/scribblings/reference/for.scrbl +++ b/collects/scribblings/reference/for.scrbl @@ -251,11 +251,12 @@ case, the procedure result of @scheme[clause-transform-expr] is called to transform the clause. When @scheme[id] is used in any other expression position, the result -of @scheme[expr-transform-expr] is used; if it is an identifier -@scheme[_other-id], then any use of @scheme[id] is converted to a use -of @scheme[_other-id]; otherwise,@scheme[expr-transform-expr] must -produce a procedure that is used as a macro transformer. - +of @scheme[expr-transform-expr] is used. If it is a procedure of zero +arguments, then the result must be an identifier @scheme[_other-id], +and any use of @scheme[id] is converted to a use of +@scheme[_other-id]. Otherwise,@scheme[expr-transform-expr] must +produce a procedure (of one argument) that is used as a macro +transformer. When the @scheme[clause-transform-expr] transformer is used, it is given a @scheme[_clause] as an argument, where the clause's form is @@ -298,10 +299,14 @@ spliced into the iteration essentially as follows: ] where @scheme[_body-bindings] and @scheme[_done-expr] are from the -context of the @scheme[:do-in] use. The actual @scheme[loop] binding -and call has additional loop arguments to support iterations in -parallel with the @scheme[:do-in] form, and the other pieces are -similarly accompanied by pieces form parallel iterations.} +context of the @scheme[:do-in] use. The identifiers bound by the +@scheme[for] clause are typically part of the @scheme[([(inner-id ...) +inner-expr] ...)] section. + +The actual @scheme[loop] binding and call has additional loop +arguments to support iterations in parallel with the @scheme[:do-in] +form, and the other pieces are similarly accompanied by pieces form +parallel iterations.} @section{Do Loops} diff --git a/collects/scribblings/reference/hashes.scrbl b/collects/scribblings/reference/hashes.scrbl new file mode 100644 index 0000000000..ad6165e4c1 --- /dev/null +++ b/collects/scribblings/reference/hashes.scrbl @@ -0,0 +1,273 @@ +#lang scribble/doc +@(require "mz.ss") + +@title[#:tag "hashtables"]{Hash Tables} + +A @deftech{hash table} (or simply @deftech{hash}) maps each of its +keys to a single value. For a given hash table, keys are equivalent +via @scheme[equal?] or @scheme[eq?], and keys are retained either +strongly or weakly (see @secref["weakbox"]). A hash table is also +either mutable or immutable; immutable tables support constant-time +functional update. + +A hash table can be used as a two-valued @tech{sequence} (see +@secref["sequences"]). The keys and values of the hash table serve as +elements of the sequence (i.e., each element is a key and its +associated value). If a mapping is added to or removed from the hash +table during iteration, then an iteration step may fail with +@scheme[exn:fail:contract], or the iteration may skip or duplicate +keys and values. See also @scheme[in-hash], @scheme[in-hash-keys], +@scheme[in-hash-values], and @scheme[in-hash-pairs]. + +Two hash tables are @scheme[equal?] if they use the same +key-comparison procedure (@scheme[equal?] or @scheme[eq?]), if they +both hold keys strongly or weakly, and if they have the same +mutability. + +@bold{Caveats concerning concurrent modification:} A mutable hash +table can be manipulated with @scheme[hash-ref], @scheme[hash-set!], +and @scheme[hash-remove!] concurrently by multiple threads, and the +operations are protected by a table-specific semaphore as needed. Two +caveats apply, however: + + @itemize{ + + @item{If a thread is terminated while applying @scheme[hash-ref], + @scheme[hash-set!], or @scheme[hash-remove!] to a hash table that + uses @scheme[equal?] key comparisons, all current and future + operations on the hash table block indefinitely.} + + @item{The @scheme[hash-map] and @scheme[hash-for-each] procedures do + not use the table's semaphore. Consequently, if a hash table is + extended with new keys by another thread while a map or for-each + traversal is in process, arbitrary key--value pairs can be dropped + or duplicated in the traversal. Similarly, if a map or for-each + procedure itself extends the table, arbitrary key--value pairs can + be dropped or duplicated. However, key mappings can be deleted or + remapped by any thread with no adverse affects (i.e., the change + does not affect a traversal if the key has been seen already, + otherwise the traversal skips a deleted key or uses the remapped + key's new value).} + + } + +@bold{Caveat concerning mutable keys:} If a key into an +@scheme[equal?]-based hash table is mutated (e.g., a key string is +modified with @scheme[string-set!]), then the hash table's behavior +for insertion and lookup operations becomes unpredictable. + + +@defproc[(hash? [v any/c]) boolean?]{ + +Returns @scheme[#t] if @scheme[v] is a @tech{hash table}, @scheme[#f] +otherwise.} + +@defproc[(hash-eq? [hash hash?]) boolean?]{ + +Returns @scheme[#t] if @scheme[hash] compares keys with @scheme[eq?], +@scheme[#f] if it compares with @scheme[equal?].} + + +@defproc[(hash-weak? [hash hash?]) boolean?]{ + +Returns @scheme[#t] if @scheme[hash] retains its keys weakly, +@scheme[#f] if it retains keys strongly.} + + +@defproc[(make-hash) hash?]{ + +Creates an empty mutable hash table that holds keys strongly and that +uses @scheme[equal?] to compare keys. See also +@scheme[make-custom-hash].} + + +@defproc[(make-hasheq) (and/c hash? hash-eq?)]{ + +Creates an empty mutable hash table that holds keys strongly and that +uses @scheme[eq?] to compare keys.} + + +@defproc[(make-weak-hash) (and/c hash? hash-weak?)]{ + +Creates an empty mutable hash table that holds keys weakly and that +uses @scheme[equal?] to compare keys. See also +@scheme[make-weak-custom-hash].} + + +@defproc[(make-weak-hasheq) (and/c hash? hash-eq? hash-weak?)]{ + +Creates an empty mutable hash table that holds keys weakly and that +uses @scheme[eq?] to compare keys.} + + +@defproc[(make-immutable-hash [assocs (listof pair?)]) + (and/c hash? immutable?)]{ + +Creates an immutable hash table that compares keys with +@scheme[equal?]. In each element of @scheme[assocs], the @scheme[car] +of each pair is a key, and the @scheme[cdr] is the corresponding +value. The mappings are added to the table in the order that they +appear in @scheme[assocs], so later mappings can hide earlier +mappings.} + +@defproc[(make-immutable-hasheq [assocs (listof pair?)]) + (and/c hash? hash-eq? immutable?)]{ + +Like @scheme[make-immutable-hash], but the resulting hash table +compares keys with @scheme[eq?].} + + +@defproc[(hash-set! [hash (and/c hash? (not/c immutable?))] + [key any/c] + [v any/c]) void?]{ + +Maps @scheme[key] to @scheme[v] in @scheme[hash], overwriting +any existing mapping for @scheme[key].} + + +@defproc[(hash-set [hash (and/c hash? immutable?)] + [key any/c] + [v any/c]) + (and/c hash? immutable?)]{ + +Functionally extends @scheme[hash] by mapping @scheme[key] to +@scheme[v], overwriting any existing mapping for @scheme[key], and +returning an extended hash table.} + + +@defproc[(hash-ref [hash hash?] + [key any/c] + [failure-result any/c (lambda () (raise (make-exn:fail ....)))]) + any]{ + +Returns the value for @scheme[key] in @scheme[hash]. If no value +is found for @scheme[key], then @scheme[failure-result] determines the +result: + +@itemize{ + + @item{If @scheme[failure-result] is a procedure, it is called + (through a tail call) with no arguments to produce the result.} + + @item{Otherwise, @scheme[failure-result] is returned as the result.} + +}} + + +@defproc[(hash-remove! [hash (and/c hash? (not/c immutable?))] + [key any/c]) + void?]{ + +Removes any existing mapping for @scheme[key] in @scheme[hash].} + + +@defproc[(hash-remove [hash (and/c hash? immutable?)] + [key any/c]) + (and/c hash? immutable?)]{ + +Functionally removes any existing mapping for @scheme[key] in +@scheme[hash], returning the updated hash table.} + + +@defproc[(hash-map [hash hash?] + [proc (any/c any/c . -> . any/c)]) + (listof any/c)]{ + +Applies the procedure @scheme[proc] to each element in +@scheme[hash] in an unspecified order, accumulating the results +into a list. The procedure @scheme[proc] is called each time with a +key and its value. See the caveat above about concurrent +modification.} + + +@defproc[(hash-for-each [hash hash?] + [proc (any/c any/c . -> . any)]) + void?]{ + +Applies @scheme[proc] to each element in @scheme[hash] (for the +side-effects of @scheme[proc]) in an unspecified order. The procedure +@scheme[proc] is called each time with a key and its value. See the +caveat above about concurrent modification.} + + +@defproc[(hash-count [hash hash?]) + nonnegative-exact-integer?]{ + +Returns the number of keys mapped by @scheme[hash]. If +@scheme[hash] is not created with @scheme['weak], then the +result is computed in constant time and atomically. If +@scheme[hash] is created with @scheme['weak], see the caveat +above about concurrent modification.} + + +@defproc[(hash-iterate-first [hash hash?]) + (or/c false/c nonnegative-exact-integer?)]{ + +Returns @scheme[#f] if @scheme[hash] contains no elements, otherwise +it returns an integer that is a index to the first element in the hash +table; ``first'' refers to an unspecified ordering of the table +elements, and the index values are not necessarily consecutive +integers. For a mutable @scheme[hash], this index is guaranteed to +refer to the first item only as long as no items are added to or +removed from @scheme[hash].} + +@defproc[(hash-iterate-next [hash hash?] + [pos nonnegative-exact-integer?]) + (or/c false/c nonnegative-exact-integer?)]{ + +Returns either an integer that is an index to the element in +@scheme[hash] after the element indexed by @scheme[pos] (which is not +necessarily one more than @scheme[pos]) or @scheme[#f] if @scheme[pos] +refers to the last element in @scheme[hash]. If @scheme[pos] is not a +valid index, then the @exnraise[exn:fail:contract]. For a mutable +@scheme[hash], the result index is guaranteed to refer to its item +only as long as no items are added to or removed from @scheme[hash].} + + +@defproc[(hash-iterate-key [hash hash?] + [pos nonnegative-exact-integer?]) + any]{ + +Returns the key for the element in @scheme[hash] at index +@scheme[pos]. If @scheme[pos] is not a valid index for +@scheme[hash], the @exnraise[exn:fail:contract].} + + +@defproc[(hash-iterate-value [hash hash?] + [pos nonnegative-exact-integer?]) + any]{ + +Returns the value for the element in @scheme[hash] at index +@scheme[pos]. If @scheme[pos] is not a valid index for +@scheme[hash], the @exnraise[exn:fail:contract].} + + +@defproc[(hash-copy [hash hash?]) + (and/c hash? (not/c immutable?))]{ + +Returns a mutable hash table with the same mappings, same +key-comparison mode, and same key-holding strength as @scheme[hash].} + + +@defproc[(eq-hash-code [v any/c]) exact-integer?]{ + +Returns an exact integer; for any two @scheme[eq?] values, the +returned integer is the same. Furthermore, for the result integer +@scheme[k] and any other exact integer @scheme[j], @scheme[(= k j)] +implies @scheme[(eq? k j)].} + + +@defproc[(equal-hash-code [v any/c]) exact-integer?]{ + +Returns an exact integer; for any two @scheme[equal?] values, the +returned integer is the same. Furthermore, for the result integer +@scheme[k] and any other exact integer @scheme[j], @scheme[(= k j)] +implies @scheme[(eq? k j)]. A has code is computed even when +@scheme[v] contains a cycle through pairs, vectors, boxes, and/or +inspectable structure fields. See also @scheme[prop:equal+hash].} + +@defproc[(equal-secondary-hash-code [v any/c]) exact-integer?]{ + +Like @scheme[equal-hash-code], but computes a secondary value suitable +for use in double hashing.} + diff --git a/collects/scribblings/reference/model.scrbl b/collects/scribblings/reference/model.scrbl index 9fc474df73..b2f5100d9b 100644 --- a/collects/scribblings/reference/model.scrbl +++ b/collects/scribblings/reference/model.scrbl @@ -1,772 +1,9 @@ #lang scribble/doc -@(require scribble/struct - (for-syntax scheme/base) - "mz.ss" - "prog-steps.ss") +@(require "mz.ss") -@(define reduces (make-element #f (list 'rarr))) -@(define rspace (make-element "ghost" (list 'rarr))) +@title[#:tag "model" #:style 'toc]{Language Model} -@(define *redex (lambda (c) - (make-element "highlighted" (list c)))) -@(define-syntax redex - (syntax-rules () [(_ a) (*redex (scheme a))])) +@local-table-of-contents[] - -@(define hole (make-element #f (list "[]"))) -@(define (*sub c e) (make-element #f (list c "[" e "]"))) -@(define langle (make-element 'tt (list "<"))) -@(define rangle (make-element 'tt (list ">"))) -@(define comma (make-element 'tt (list ", "))) -@(define-syntax sub - (syntax-rules () [(_ a b) (*sub (scheme a) (scheme b))])) -@(define (*state c e) (make-element #f (list langle c comma e rangle))) -@(define-syntax state - (syntax-rules () [(_ a b) (*state (scheme a) (scheme b))])) -@(define (frame n) - (make-element "schemevariable" - (list "C" (make-element 'subscript (list (format "~a" n)))))) - -@;------------------------------------------------------------------------ -@title{Evaluation Model} - -Scheme evaluation can be viewed as the simplification of expressions -to obtain values. For example, just as an elementary-school student -simplifies - -@verbatim{ 1 + 1 = 2} - -Scheme evaluation simplifies - -@schemeblock[ -(+ 1 1) #, @reduces 2 -] - -The arrow @reduces above replaces the more traditional @tt{=} to -emphasize that evaluation proceeds in a particular direction towards -simpler expressions. In particular, a @deftech{value} is an -expression that evaluation simplifies no further, such as the number -@scheme[2]. - -@;------------------------------------------------------------------------ -@section[#:tag "cont-model"]{Sub-expression Evaluation and Continuations} - -Some simplifications require more than one step. For example: - -@schemeblock[ -(- 4 #,(redex (+ 1 1))) #,reduces #,(redex (- 4 2)) #,reduces 2 -] - -An expression that is not a @tech{value} can always be partitioned -into two parts: a @deftech{redex}, which is the part that changed in a -single-step simplification (show in blue), and the -@deftech{continuation}, which is the surrounding expression -context. In @scheme[(- 4 (+ 1 1))], the redex is @scheme[(+ 1 1)], and -the continuation is @scheme[(- 4 #, @hole)], where @hole takes the -place of the redex. That is, the continuation says how to ``continue'' -after the @tech{redex} is reduced to a @tech{value}. - -Before some things can be evaluated, some sub-expressions must be -evaluated; for example, in the application @scheme[(- 4 (+ 1 1))], the -application of @scheme[-] cannot be reduced until the sub-expression -@scheme[(+ 1 1)] is reduced. - -Thus, the specification of each syntactic form specifies how (some of) -its sub-expressions are evaluated, and then how the results are -combined to reduce the form away. - -The @deftech{dynamic extent} of an expression is the sequence of -evaluation steps during which an expression contains the @tech{redex}. - -@;------------------------------------------------------------------------ -@section{Tail Position} - -An expression @scheme[_expr1] is in @deftech{tail position} with -respect to an enclosing expression @scheme[_expr2] if, whenever -@scheme[_expr1] becomes a redex, its @tech{continuation} is the same -as was the enclosing @scheme[_expr2]'s @tech{continuation}. - -For example, the @scheme[(+ 1 1)] expression is @italic{not} in @tech{tail -position} with respect to @scheme[(- 4 (+ 1 1))]. To illustrate, we use -the notation @sub[_C _expr] to mean the expression that is produced by -substituting @scheme[_expr] in place of @hole in the @tech{continuation} -@scheme[_C]: - -@schemeblock[ -#, @sub[_C (- 4 (+ 1 1))] #, @reduces #, @sub[_C (- 4 2)] -] - -In this case, the @tech{continuation} for reducing @scheme[(+ 1 1)] is -@sub[_C (+ 4 #, @hole)], not just @scheme[_C]. - -In contrast, @scheme[(+ 1 1)] is in @tech{tail position} with respect -to @scheme[(if (zero? 0) (+ 1 1) 3)], because, for any continuation -@scheme[_C], - -@schemeblock[ -#, @sub[_C (if (zero? 0) (+ 1 1) 3)] #, @reduces #, @sub[_C (if #t (+ 1 1) 3)] #, @reduces #, @sub[_C (+ 1 1)] -] - -The steps in this reduction sequence are driven by the definition of -@scheme[if], and they do not depend on the @tech{continuation} -@scheme[_C]. The ``then'' branch of an @scheme[if] form is always in -@tech{tail position} with respect to the @scheme[if] form. Due to a -similar reduction rule for @scheme[if] and @scheme[#f], the ``else'' -branch of an @scheme[if] form is also in @tech{tail position}. - -@tech{Tail-position} specifications provide a guarantee about the -asymptotic space consumption of a computation. In general, the -specification of @tech{tail positions} goes with each syntactic form, -like @scheme[if]. - -@;------------------------------------------------------------------------ -@section[#:tag "values-model"]{Multiple Return Values} - -A Scheme expression can evaluate to @deftech{multiple values}, in the -same way that a procedure can accept multiple arguments. - -Most @tech{continuations} expect a particular number of result -@tech{values}. Indeed, most @tech{continuations}, such as @scheme[(+ -#, @hole 1)] expect a single @tech{value}. The @tech{continuation} -@scheme[(let-values ([(x y) #, @hole]) _expr)] expects two result -@tech{values}; the first result replaces @scheme[x] in the body -@scheme[_expr], and the second replaces @scheme[y] in -@scheme[_expr]. The @tech{continuation} @scheme[(begin #, @hole (+ 1 -2))] accepts any number of result @tech{values}, because it ignores -the result(s). - -In general, the specification of a syntactic form inidicates the -number of @tech{values} that it produces and the number that it -expects from each of its sub-expression. In addtion, some procedures -(notably @scheme[values]) produce multiple @tech{values}, and some -procedures (notably @scheme[call-with-values]) create continuations -internally that accept a certain number of @tech{values}. - -@;------------------------------------------------------------------------ -@section{Top-Level Variables} - -Given - -@verbatim{ x = 10} - -then an algebra student simplifies @tt{x + 1} as follows: - -@verbatim{ x + 1 = 10 + 1 = 11} - -Scheme works much the same way, in that a set of @tech{top-level -variables} are available for substitutions on demand during -evaluation. For example, given - -@schemeblock[ -(define x 10) -] - -then - -@schemeblock[ -#,(redex (+ x 1)) #,reduces #,(redex (+ 10 1)) #,reduces 11 -] - -In Scheme, the way definitions appear is just as important as the way -that they are used. Scheme evaluation thus keeps track of both -definitions and the current expression, and it extends the set of -definitions in response to evaluating forms such as @scheme[define]. - -Each evaluation step, then, takes the current set of definitions and -program to a new set of definitions and program. Before a -@scheme[define] can be moved into the set of definitions, its -right-hand expression must be reduced to a @tech{value}. - -@prog-steps/no-obj[ -[{} - (begin (define x (code:hilite (+ 9 1))) (+ x 1))] -[{} - (begin (code:hilite (define x 10)) (+ x 1))] -[{(define x 10)} - (code:hilite (begin (void) (+ x 1)))] -[{(define x 10)} - (+ (code:hilite x) 1)] -[{(define x 10)} - (code:hilite (+ 10 1))] -[{(define x 10)} - 11] -] - -Using @scheme[set!], a program can change the value associated with an -existing @tech{top-level variable}: - -@prog-steps/no-obj[ -[{(define x 10)} - (begin (code:hilite (set! x 8)) x)] -[{(define x 8)} - (code:hilite (begin (void) x))] -[{(define x 8)} - (code:hilite x)] -[{(define x 8)} - 8] -] - -@;------------------------------------------------------------------------ -@section{Objects and Imperative Update} - -In addition to @scheme[set!] for imperative update of @tech{top-level -variables}, various procedures enable the modification of elements -within a compound data structure. For example, @scheme[vector-set!] -modifies the content of a vector. - -To allow such modifications to data, we must distingiush between -@tech{values}, which are the results of expressions, and -@deftech{objects}, which hold the data referenced by a value. - -A few kinds of @tech{objects} can serve directly as values, including -booleans, @scheme[(void)], and small exact integers. More generally, -however, a @tech{value} is a reference to an @tech{object}. For -example, a @tech{value} can be a reference to a particular vector that -currently holds the value @scheme[10] in its first slot. If an -@tech{object} is modified, then the modification is visible through -all copies of the @tech{value} that reference the same @tech{object}. - -In the evaluation model, a set of @tech{objects} must be carried along -with each step in evaluation, just like the definition set. Operations -that create @tech{objects}, such as @scheme[vector], add to the set of -@tech{objects}: - -@prog-steps[ -[{} - {} - (begin (define x (code:hilite (vector 10 20))) - (define y x) - (vector-set! x 0 11) - (vector-ref y 0))] -[{(define (vector 10 20))} - {} - (begin (code:hilite (define x )) - (define y x) - (vector-set! x 0 11) - (vector-ref y 0))] -[{(define (vector 10 20))} - {(define x )} - (code:hilite (begin (void) - (define y x) - (vector-set! x 0 11) - (vector-ref y 0)))] -[{(define (vector 10 20))} - {(define x )} - (begin (define y (code:hilite x)) - (vector-set! x 0 11) - (vector-ref y 0))] -[{(define (vector 10 20))} - {(define x )} - (begin (code:hilite (define y )) - (vector-set! x 0 11) - (vector-ref y 0))] -[{(define (vector 10 20))} - {(define x ) - (define y )} - (code:hilite (begin (void) - (vector-set! x 0 11) - (vector-ref y 0)))] -[{(define (vector 10 20))} - {(define x ) - (define y )} - (begin (vector-set! (code:hilite x) 0 11) - (vector-ref y 0))] -[{(define (vector 10 20))} - {(define x ) - (define y )} - (begin (code:hilite (vector-set! 0 11)) - (vector-ref y 0))] -[{(define (vector 11 20))} - {(define x ) - (define y )} - (code:hilite (begin (void) - (vector-ref y 0)))] -[{(define (vector 11 20))} - {(define x ) - (define y )} - (vector-ref (code:hilite y) 0)] -[{(define (vector 11 20))} - {(define x ) - (define y )} - (code:hilite (vector-ref 0))] -[{(define (vector 11 20))} - {(define x ) - (define y )} - 11] -] - -The distinction between a @tech{top-level variable} and an object -reference is crucial. A @tech{top-level variable} is not a -@tech{value}; each time a @tech{variable} expression is evaluated, the -value is extracted from the current set of definitions. An object -reference, in contrast is a value, and therefore needs no further -evaluation. The model evaluation steps above use angle-bracketed -@scheme[] for an object reference to distinguish it from a -@tech{variable} name. - -A direct object reference can never appear in a text-based source -program. A program representation created with -@scheme[datum->syntax-object], however, can embed direct references to -existing @tech{objects}. - -@;------------------------------------------------------------------------ -@section[#:tag "model-eq"]{Object Identity and Comparisons} - -The @scheme[eq?] operator compares two @tech{values}, returning -@scheme[#t] when the values refer to the same @tech{object}. This form -of equality is suitable for comparing objects that support imperative -update (e.g., to determine that the effect of modifying an object -through one reference is visible through another reference). Also, an -@scheme[eq?] test evaluates quickly, and @scheme[eq?]-based hashing -is more lightweight than @scheme[equal?]-based hashing in hash tables. - -In some cases, however, @scheme[eq?] is unsuitable as a comparison -operator, because the generation of @tech{objects} is not clearly -defined. In particular, two applications of @scheme[+] to the same two -exact integers may or may not produce results that are @scheme[eq?], -although the results are always @scheme[equal?]. Similarly, evaluation -of a @scheme[lambda] form typically generates a new procedure -@tech{object}, but it may re-use a procedure @tech{object} previously -generated by the same source @scheme[lambda] form. - -The behavior of a datatype with respect to @scheme[eq?] is generally -specified with the datatype and its associated procedures. - -@;------------------------------------------------------------------------ -@section[#:tag "gc-model"]{Garbage Collection} - -In the program state - -@prog-steps[ -[{(define (vector 10 20)) - (define (vector 0))} - {(define x )} - (+ 1 x)] -] - -evaluation cannot depend on @scheme[], because it is not part of -the program to evaluate, and it is not referenced by any definition -that is accessible in the program. The @tech{object} @scheme[] may -therefore be removed from the evaluation by @deftech{garbage -collection}. - -A few special compound datatypes hold @deftech{weak references} to -objects. Such weak references are treated specially by the garbage -collector in determining which @tech{objects} are reachable for the -remainder of the computation. If an @tech{object} is reachable only -via a @tech{weak reference}, then the object can be reclaimed, and the -@tech{weak reference} is replaced by a different @tech{value} -(typically @scheme[#f]). - -@;------------------------------------------------------------------------ -@section{Procedure Applications and Local Variables} - -Given - -@verbatim{ f(x) = x + 10} - -then an algebra student simplifies @tt{f(1)} as follows: - -@verbatim{ f(7) = 7 + 10 = 17} - -The key step in this simplification is take the body of the defined -function @tt{f}, and then replace each @tt{x} with the actual -@tech{value} @tt{1}. - -Scheme procedure application works much the same way. A procedure is -an @tech{object}, so evaluating @scheme[(f 7)] starts with a -@tech{variable} lookup: - -@prog-steps[ -[{(define (lambda (x) (+ x 10)))} - {(define f )} - ((code:hilite f) 7)] -[{(define (lambda (x) (+ x 10)))} - {(define f )} - (code:hilite ( 7))] -] - -Unlike in algebra, however, the @tech{value} associated with an -argument can be changed in the body of a procedure by using -@scheme[set!], as in the example @scheme[(lambda (x) (begin (set! x 3) -x))]. Since the @tech{value} associated with @scheme[x] can be -changed, an actual value for cannot be substituted for @scheme[x] when -the procedure is applied. - -Instead, a new @deftech{location} is created for each @tech{variable} -on each application. The argument @tech{value} is placed in the -@tech{location}, and each instance of the @tech{variable} in the -procedure body is replaced with the new @tech{location}: - -@prog-steps[ -[{(define (lambda (x) (+ x 10)))} - {(define f )} - (code:hilite ( 7))] -[{(define (lambda (x) (+ x 10)))} - {(define f ) - (define xloc 7)} - (+ (code:hilite xloc) 10)] -[{(define (lambda (x) (+ x 10)))} - {(define f ) - (define xloc 7)} - (code:hilite (+ 7 10))] -[{(define (lambda (x) (+ x 10)))} - {(define f ) - (define xloc 7)} - 17] -] - -A @tech{location} is the same as a @tech{top-level variable}, but when -a @tech{location} is generated, it (conceptually) uses a name that has -not been used before and that cannot not be generated again or -accessed directly. - -Generating a @tech{location} in this way means that @scheme[set!] -evaluates for @tech{local variables} in the same way as for -@tech{top-level variables}, because the @tech{local variable} is -always replaced with a @tech{location} by the time the @scheme[set!] -form is evaluated: - -@prog-steps[ -[{(define (lambda (x) (begin (set! x 3) x)))} - {(define f )} - ((code:hilite f) 7)] -[{(define (lambda (x) (begin (set! x 3) x)))} - {(define f )} - (code:hilite ( 7))] -[{(define (lambda (x) (begin (set! x 3) x)))} - {(define f ) - (define xloc 7)} - (begin (code:hilite (set! xloc 3)) xloc)] -[{(define (lambda (x) (begin (set! x 3) x)))} - {(define f ) - (define xloc 3)} - (code:hilite (begin (void) xloc))] -[{(define (lambda (x) (begin (set! x 3) x)))} - {(define f ) - (define xloc 3)} - (code:hilite xloc)] -[{(define (lambda (x) (begin (set! x 3) x)))} - {(define f ) - (define xloc 3)} - 3] -] - -The substition and @tech{location}-generation step of procedure -application requires that the argument is a @tech{value}. Therefore, -in @scheme[((lambda (x) (+ x 10)) (+ 1 2))], the @scheme[(+ 1 2)] -sub-expression must be simplified to the @tech{value} @scheme[3], and -then @scheme[3] can be placed into a @tech{location} for -@scheme[x]. In other words, Scheme is a @deftech{call-by-value} -language. - -Evaluation of a local-variable form, such as @scheme[(let ([x (+ 1 -2)]) _expr)], is the same as for a procedure call. After @scheme[(+ 1 -2)] produces a @tech{value}, it is stored in a fresh @tech{location} -that replaces every instance of @scheme[x] in @scheme[_expr]. - -@;------------------------------------------------------------------------ -@section{Variables and Locations} - -A @deftech{variable} is a placeholder for a @tech{value}, and an -expressions in an initial program refer to @tech{variables}. A -@deftech{top-level variable} is both a @tech{variable} and a -@tech{location}. Any other @tech{variable} is always replaced by a -@tech{location} at run-time, so that evaluation of expressions -involves only @tech{locations}. A single @deftech{local variable} -(i.e., a non-top-level, non-module-level @tech{variable}), such as a -procedure argument, can correspond to different @tech{locations} -through different instantiations. - -For example, in the program - -@schemeblock[(define y (+ (let ([x 5]) x) 6))] - -both @scheme[y] and @scheme[x] are @tech{variables}. The @scheme[y] -@tech{variable} is a @tech{top-level variable}, and the @scheme[x] is -a @tech{local variable}. When this code is evaluated, a -@tech{location} is created for @scheme[x] to hold the value -@scheme[5], and a @tech{location} is also created for @scheme[y] to -hold the value @scheme[6]. - -The replacement of a @tech{variable} with a @tech{location} during -evaluation implements Scheme's @deftech{lexical scoping}. For example, -when a procedure-argument @tech{variable} @scheme[x] is replaced by -the @tech{location} @scheme[xloc], then it is replaced throughout the -body of the procedure, including with any nested @scheme[lambda] -forms. As a result, future references of the @tech{variable} always -access the same @tech{location}. - -@;------------------------------------------------------------------------ -@section[#:tag "module-eval-model"]{Modules and Module-Level Variables} - -Most definitions in PLT Scheme are in modules. In terms of evaluation, -a module is essentially a prefix on a defined name, so that different -modules can define the name. That is, a @deftech{module-level -variable} is like a @tech{top-level variable} from the perspective of -evaluation. - -One difference between a module an a top-level definition is that a -module can be declared without instantiating its module-level -definitions. Evaluation of a @scheme[require] @deftech{instantiates} -(i.e., triggers the @deftech{instantiation} of) a declared module, -which creates variables that correspond to its module-level -definitions. - -For example, given the module declaration - -@schemeblock[ -(module m mzscheme - (define x 10)) -] - -the evaluation of @scheme[(require m)] creates the variable @scheme[x] -and installs @scheme[10] as its value. This @scheme[x] is unrelated to -any top-level definition of @scheme[x]. - -@;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -@subsection[#:tag "module-phase"]{Module Phases} - -A module can be @tech{instantiate}d in multiple @deftech{phases}. A -phase is an integer that, again, is effectively a prefix on the names -of module-level definitions. A top-level @scheme[require] -@tech{instantiates} a module at @tech{phase} 0, if the module is not -already @tech{instantiate}d at phase 0. A top-level -@scheme[require-for-syntax] @tech{instantiates} a module at -@tech{phase} 1 (if it is not already @tech{instantiate}d at that -level); a @scheme[require-for-syntax] also has a different binding -effect on further program parsing, as described in -@secref["intro-binding"]. - -Within a module, some definitions are shifted by a phase already; the -@scheme[define-for-syntax] form is like @scheme[define], but it -defines a variable at relative @tech{phase} 1, instead of relative -@tech{phase} 0. Thus, if the module is @tech{instantiate}d at phase 1, -the variables for @scheme[define-for-syntax] are created at phase 2, -and so on. Moreover, this relative phase acts as another layer of -prefixing, so that a @scheme[define] of @scheme[x] and a -@scheme[define-for-syntax] of @scheme[x] can co-exist in a module -without colliding. Again, the higher phases are mainly related to -program parsing, instead of normal evaluation. - -If a module @tech{instantiate}d at @tech{phase} @math{n} -@scheme[require]s another module, then the @scheme[require]d module is -first @tech{instantiate}d at phase @math{n}, and so on -transitively. (Module @scheme[require]s cannot form cycles.) If a -module @tech{instantiate}d at phase @math{n} -@scheme[require-for-syntax]es another module, the other module is -first @tech{instantiate}d at @tech{phase} @math{n+1}, and so on. If a -module @tech{instantiate}d at phase @math{n} for non-zero @math{n} -@scheme[require-for-template]s another module, the other module is -first @tech{instantiate}d at @tech{phase} @math{n-1}, and so on. - -A final distinction among module @tech{instantiations} is that -multiple @tech{instantiations} may exist at phase 1 and higher. These -@tech{instantiations} are created by the parsing of module forms (see -@secref["mod-parse"]), and are, again, conceptually distinguished -by prefixes. - -@;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -@subsection[#:tag "module-redeclare"]{Module Re-declarations} - -@section-index["modules" "re-define"] - -When a module is declared using a name for which a module is already -declared, the new declaration's definitions replace and extend the old -declarations. If a variable in the old declaration has no counterpart -in the new declaration, the old variable continues to exist, but its -binding is not included in the @tech{lexical information} for the -module body. If a new variable definition has a counterpart in the old -declaration, it effectively assigns to the old variable. - -If a module is @tech{instantiate}d in any @tech{phase}s before it is -re-declared, each re-declaration of the module is immediately -@tech{instantiate}d in the same @tech{phase}s. - -@;------------------------------------------------------------------------ -@section[#:tag "mark-model"]{Continuation Frames and Marks} - -Every continuation @scheme[_C] can be partitioned into -@deftech{continuation frames} @frame[1], @frame[2], ..., @frame["n"] -such that @scheme[_C] = @*sub[@frame[1] @*sub[@frame[2] @*sub["..." -@frame["n"]]]], and no frame @frame["i"] can be itself partitioned -into smaller continuations. Evaluation steps add and remove frames to -the current continuation, typically one at a time. - -Each frame is conceptually annotated with a set of -@deftech{continuation marks}. A mark consists of a key and its value; -the key is an arbitrary value, and each frame includes at most one -mark for any key. Various operations set and extract marks from -continuations, so that marks can be used to attach information to a -dynamic extent. For example, marks can be used to record information -for a ``stack trace'' to be used when an exception is raised, or -to implement dynamic scope. - -@;------------------------------------------------------------------------ -@section[#:tag "prompt-model"]{Prompts, Delimited Continuations, and Barriers} - -A @deftech{prompt} is a special kind of continuation frame that is -annotated with a specific @deftech{prompt-tag} (essentially a -continuation mark). Various operations allow the capture of frames in -the continuation from the redex position out to the nearest enclosing -prompt with a particular prompt tag; such a continuation is sometimes -called a @deftech{delimited continuation}. Other operations allow the -current continuation to be extended with a captured continuation -(specifically, a @deftech{composable continuation}). Yet other -operations abort the computation to the nearest enclosing prompt with -a particular tag, or replace the continuation to the nearest enclosing -prompt with another one. When a delimited continuation is captured, -the marks associated with the relevant frames are also captured. - -A @deftech{continuation barrier} is another kind of continuation frame -that prohibits certain replacements of the current continuation with -another. Specifically, while an abort is allowed to remove a portion -of the continuation containing a prompt, the continuation can be -replaced by another only when the replacement also includes the -continuation barrier. Certain operations install barriers -automatically; in particular, when an exception handler is called, a -continuation barrier prohibits the continuation of the handler from -capturing the continuation past the exception point. - -A @deftech{escape continuation} is essentially a derived concept. It -combines a prompt for escape purposes with a continuation for -mark-gathering purposes. as the name implies, escape continuations are -used only to abort to the point of capture, which means that -escape-continuation aborts can cross continuation barriers. - -@;------------------------------------------------------------------------ -@section[#:tag "thread-model"]{Threads} - -Scheme supports multiple, pre-emptive @deftech{threads} of -evaluation. In terms of the evaluation model, this means that each -step in evaluation actually consists of multiple concurrent -expressions, rather than a single expression. The expressions all -share the same objects and top-level variables, so that they can -communicate through shared state. Most evaluation steps involve a -single step in a single expression, but certain synchronization -primitives require multiple threads to progress together in one step. - -In addition to the state that is shared among all threads, each thread -has its own private state that is accessed through @deftech{thread -cells}. A thread cell is similar to a normal mutable object, but a -change to the value inside a thread cell is seen only when extracting -a value from the cell from the same thread. A thread cell can be -@deftech{preserved}; when a new thread is created, the creating -thread's value for a preserved thread cell serves as the initial value -for the cell in the created thread. For a non-preserved thread cell, a -new thread sees the same initial value (specified when the thread cell -is created) as all other threads. - -@;------------------------------------------------------------------------ -@section[#:tag "parameter-model"]{Parameters} - -@deftech{Parameters} are essentially a derived concept in Scheme; they -are defined in terms of continuation marks and thread cells. However, -parameters are also built in, in the sense that some primitive -procedures consult parameter values. For example, the default output -stream for primitive output operations is determined by a parameter. - -A parameter is a setting that is both thread-specific and -continuation-specific. In the empty continuation, each parameter -corresponds to a preserved thread cell; a corresponding -@deftech{parameter procedure} accesses and sets the thread cell's -value for the current thread. - -In a non-empty continuation, a parameter's value is determined through -a @deftech{parameterization} that is associated with the nearest -enclosing continuation frame though a continuation mark (whose key is -not directly accessible). A parameterization maps each parameter to a -preserved thread cell, and the combination of thread cell and current -thread yields the parameter's value. A parameter procedure sets or -accesses the relevant thread cell for its parameter. - -Various operations, such as @scheme[parameterize] or -@scheme[with-parameterization], install a parameterization into the -current continuation's frame. - -@;------------------------------------------------------------------------ -@section[#:tag "exn-model"]{Exceptions} - -@deftech{Exceptions} are essentially a derived concept in Scheme; they -are defined in terms of continuations, prompts, and continuation -marks. However, exceptions are also built in, in the sense that -primitive forms and procedures may raise exceptions. - -A handler for uncaught exceptions is designated through a built-in -parameter. A handler to catch exceptions can be associated with a -continuation frame though a continuation mark (whose key is not -directly accessible). When an exception is raised, the current -continuation's marks determine a chain of handler procedures that are -consulted to handle the exception. - -One potential action of an exception handler is to abort the current -continuation up to an enclosing prompt with a particular tag. The -default handler for uncaught exceptions, in particular, aborts to a -particular tag for which a prompt is always present, because the -prompt is installed in the outermost frame of the continuation for any -new thread. - -@;------------------------------------------------------------------------ -@section[#:tag "custodian-model"]{Custodians} - -A @deftech{custodian} manages a collection of threads, file-stream -ports, TCP ports, TCP listeners, UDP sockets, and byte converters. -Whenever a thread, file-stream port, TCP port, TCP listener, or UDP -socket is created, it is placed under the management of the -@deftech{current custodian} as determined by the -@scheme[current-custodian] @tech{parameter}. - -@margin-note{In MrEd, custodians also manage eventspaces.} - -Except for the root custodian, every @tech{custodian} itself it -managed by a @tech{custodian}, so that custodians form a hierarchy. -Every object managed by a subordinate custodian is also managed by the -custodian's owner. - -When a @tech{custodian} is shut down via -@scheme[custodian-shutdown-all], it forcibly and immediately closes -the ports, TCP connections, etc. that it manages, as well as -terminating (or suspending) its threads. A custodian that has been -shut down cannot manage new objects. If the current custodian is shut -down before a procedure is called to create a managed resource (e.g., -@scheme[open-input-port], @scheme[thread]), the -@exnraise[exn:fail:contract]. - -A thread can have multiple managing custodians, and a suspended thread -created with @scheme[thread/suspend-to-kill] can have zero -custodians. Extra custodians become associated with a thread through -@scheme[thread-resume] (see @secref["threadkill"]). When a thread -has multiple custodians, it is not necessarily killed by a -@scheme[custodian-shutdown-all], but shut-down custodians are removed -from the thread's managing set, and the thread is killed when its -managing set becomes empty. - -The values managed by a custodian are only weakly held by the -custodian. As a result, a @tech{will} can be executed for a value that -is managed by a custodian. In addition, a custodian only weakly -references its subordinate custodians; if a subordinate custodian is -unreferenced but has its own subordinates, then the custodian may be -collected, at which point its subordinates become immediately -subordinate to the collected custodian's superordinate custodian. - -In addition to the other entities managed by a custodian, a -@defterm{custodian box} created with @scheme[make-custodian-box] -strongly holds onto a value placed in the box until the box's -custodian is shut down. The custodian only weakly retains the box -itself, however (so the box and its content can be collected if there -are no other references to them). - -When MzScheme is compiled with support for per-custodian memory -accounting (see @scheme[custodian-memory-accounting-available?]), the -@scheme[current-memory-use] procedure can report a custodian-specific -result. This result determines how much memory is occupied by objects -that are reachable from the custodian's managed values, especially its -threads, and including its sub-custodians' managed values. If an -object is reachable from two custodians where neither is an ancestor -of the other, an object is arbitrarily charged to one of the other, -and the choice can change after each collection; objects reachable -from both a custodian and its descendant, however, are reliably -charged to the descendant. Reachability for per-custodian accounting -does not include weak references, references to threads managed by -non-descendant custodians, references to non-descendant custodians, or -references to custodian boxes for non-descendant custodians. +@include-section["eval-model.scrbl"] +@include-section["syntax-model.scrbl"] diff --git a/collects/scribblings/reference/reference.scrbl b/collects/scribblings/reference/reference.scrbl index 976d940be8..12d747a78a 100644 --- a/collects/scribblings/reference/reference.scrbl +++ b/collects/scribblings/reference/reference.scrbl @@ -27,7 +27,6 @@ languages, where @schememodname[scheme] includes all of @table-of-contents[] @include-section["model.scrbl"] -@include-section["syntax-model.scrbl"] @include-section["syntax.scrbl"] @include-section["data.scrbl"] @include-section["struct.scrbl"] diff --git a/collects/scribblings/reference/strings.scrbl b/collects/scribblings/reference/strings.scrbl index 2c60e7b862..717adfb247 100644 --- a/collects/scribblings/reference/strings.scrbl +++ b/collects/scribblings/reference/strings.scrbl @@ -7,7 +7,7 @@ @local-table-of-contents[] -A @pidefterm{string} is a fixed-length array of +A @deftech{string} is a fixed-length array of @seclink["characters"]{characters}. @index['("strings" "immutable")]{A} string can be @defterm{mutable} or diff --git a/collects/scribblings/reference/struct.scrbl b/collects/scribblings/reference/struct.scrbl index 9b032f1a58..f27df57f8f 100644 --- a/collects/scribblings/reference/struct.scrbl +++ b/collects/scribblings/reference/struct.scrbl @@ -8,10 +8,10 @@ @guideintro["define-struct"]{structure types via @scheme[define-struct]} -A @deftech{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 +A @deftech{structure type} is a record datatype composing a number of +@idefterm{fields}. A @deftech{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 diff --git a/collects/scribblings/reference/vectors.scrbl b/collects/scribblings/reference/vectors.scrbl new file mode 100644 index 0000000000..75e8dcc7d8 --- /dev/null +++ b/collects/scribblings/reference/vectors.scrbl @@ -0,0 +1,145 @@ +#lang scribble/doc +@(require "mz.ss") + +@title[#:tag "vectors"]{Vectors} + +A @deftech{vector} is a fixed-length array with constant-time access +and update of the vector slots, which are numbered from @scheme[0] to +one less than the number of slots in the vector. + +Two vectors are @scheme[equal?] if they have the same length, and if +the values in corresponding slots of the the vectors are +@scheme[equal?]. + +A vector can be @defterm{mutable} or @defterm{immutable}. When an +immutable vector is provided to a procedure like @scheme[vector-set!], +the @exnraise[exn:fail:contract]. Vectors generated by the default +reader (see @secref["parse-string"]) are immutable. + +A vector can be used as a single-valued sequence (see +@secref["sequences"]). The elements of the vector serve as elements +of the sequence. See also @scheme[in-vector]. + +@defproc[(vector? [v any/c]) boolean?]{ + +Returns @scheme[#t] if @scheme[v] is a vector, @scheme[#f] otherwise.} + + +@defproc[(make-vector [size nonnegative-exact-integer?] + [v any/c 0]) vector?]{ + +Returns a mutable vector with @scheme[size] slots, where all slots are +initialized to contain @scheme[v].} + + +@defproc[(vector [v any/c] ...) vector?]{ + +Returns a mutable vector with as many slots as provided @scheme[v]s, +where the slots are initialized to contain the given @scheme[v]s in +order.} + + +@defproc[(vector-immutable [v any/c] ...) (and/c vector? + immutable?)]{ + +Returns an immutable vector with as many slots as provided +@scheme[v]s, where the slots are contain the given @scheme[v]s in +order.} + + + +@defproc[(vector-length [vec vector?]) nonnegative-exact-integer?]{ + +Returns the length of @scheme[vec] (i.e., the number of slots in the +vector).} + +@defproc[(vector-ref [vec vector?][pos nonnegative-exact-integer?]) any/c]{ + +Returns the element in slot @scheme[pos] of @scheme[vec]. The first +slot is position @scheme[0], and the last slot is one less than +@scheme[(vector-length vec)].} + +@defproc[(vector-set! [vec (and/c vector? (not/c immutable?))] + [pos nonnegative-exact-integer?] + [v any/c]) + void?]{ + +Updates the slot @scheme[pos] of @scheme[vec] to contain @scheme[v].} + + +@defproc[(vector->list [vec vector?]) + list?]{ + +Returns a list with the same length and elements as @scheme[vec].} + + +@defproc[(list->vector [lst list?]) + vector?]{ + +Returns a mutable vector with the same length and elements as +@scheme[lst].} + + +@defproc[(vector->immutable-vector [vec vector?]) + (and/c vector? immutable?)]{ + +Returns an immutable vector with the same length and elements as @scheme[vec]. +If @scheme[vec] is itself immutable, then it is returned as the result.} + + +@defproc[(vector-fill! [vec (and/c vector? (not/c immutable?))] + [v any/c]) + void?]{ + +Changes all slots of @scheme[vec] to contain @scheme[v].} + + +@defproc[(vector-copy! [dest (and/c vector? (not/c immutable?))] + [dest-start exact-nonnegative-integer?] + [src vector?] + [src-start exact-nonnegative-integer? 0] + [src-end exact-nonnegative-integer? (vector-length src)]) + void?]{ + + Changes the elements of @scheme[dest] starting at position + @scheme[dest-start] to match the elements in @scheme[src] from + @scheme[src-start] (inclusive) to @scheme[src-end] (exclusive). The + vectors @scheme[dest] and @scheme[src] can be the same vector, and in + that case the destination region can overlap with the source region; + the destination elements after the copy match the source elements + from before the copy. If any of @scheme[dest-start], + @scheme[src-start], or @scheme[src-end] are out of range (taking into + account the sizes of the vectors and the source and destination + regions), the @exnraise[exn:fail:contract]. + +@examples[(define v (vector 'A 'p 'p 'l 'e)) + (vector-copy! v 4 #(y)) + (vector-copy! v 0 v 3 4) + v]} + + +@defproc[(vector->values [vec vector?] + [start-pos nonnegative-exact-integer? 0] + [end-pos nonnegative-exact-integer? (vector-length vec)]) + any]{ + +Returns @math{@scheme[end-pos] - @scheme[start-pos]} values, which are +the elements of @scheme[vec] from @scheme[start-pos] (inclusive) to +@scheme[end-pos] (exclusive). If @scheme[start-pos] or +@scheme[end-pos] are greater than @scheme[(vector-length vec)], or if +@scheme[end-pos] is less than @scheme[start-pos], the +@exnraise[exn:fail:contract].} + +@defproc[(build-vector [n exact-nonnegative-integer?] + [proc (exact-nonnegative-integer? . -> . any.c)]) + vector?]{ + +Creates a vector of @scheme[n] elements by applying @scheme[proc] to +the integers from @scheme[0] to @scheme[(sub1 n)] in order. If +@scheme[_vec] is the resulting vector, then @scheme[(vector-ref _vec +_i)] is the value produced by @scheme[(proc _i)]. + +@examples[ +(build-vector 5 add1) +]} + diff --git a/collects/tests/mzscheme/dict.ss b/collects/tests/mzscheme/dict.ss new file mode 100644 index 0000000000..517a68e235 --- /dev/null +++ b/collects/tests/mzscheme/dict.ss @@ -0,0 +1,109 @@ + +(load-relative "loadtest.ss") + +(Section 'dict) + +(require scheme/dict) + +;; Currently relying on the `map' an `for-each' to test `dict-iterate-...', +;; and custom hashes to test `prop:dict' use. + +(define (try-simple d mutable? can-remove? can-update? [orig-one 1]) + (test #t dict? d) + + (test 'one dict-ref d 1) + (test 'nope dict-ref d 100 'nope) + (test 'nope dict-ref d 100 (lambda () 'nope)) + + (test #t ormap values (dict-map d (lambda (k v) (equal? k orig-one)))) + (test #t ormap values (dict-map d (lambda (k v) (equal? v 'one)))) + (test #f ormap values (dict-map d (lambda (k v) (equal? k 100)))) + + (test mutable? dict-mutable? d) + (test can-remove? dict-can-remove-keys? d) + (test can-update? dict-can-functional-set? d) + + (test (dict-map d cons) 'in-dict + (for/list ([(k v) (in-dict d)]) + (cons k v))) + (test (dict-map d cons) 'in-dict/keys/vals + (for/list ([k (in-dict-keys d)] + [v (in-dict-values d)]) + (cons k v))) + (test (dict-map d cons) 'in-dict-pairs + (for/list ([p (in-dict-pairs d)]) + p)) + + (let ([l null]) + (dict-for-each d (lambda (k v) (set! l (cons (cons k v) l)))) + (test (reverse l) dict-map d cons) + (test (length l) dict-count d)) + + (if (not can-remove?) + (begin + (err/rt-test (dict-remove! d 1)) + (err/rt-test (dict-remove d 1)) + (err/rt-test (dict-set d 1 "ONE")) + (test (void) dict-set! d 1 "ONE") + (test "ONE" dict-ref d 1)) + (let ([cnt (dict-count d)] + [smaller (if mutable? + (begin + (err/rt-test (dict-remove d 1)) + (dict-remove! d 1) + d) + (begin + (err/rt-test (dict-remove! d 1)) + (dict-remove d 1)))]) + (test 'nope dict-ref smaller 1 'nope) + (test (sub1 cnt) dict-count smaller) + (let ([try-add + (lambda (d val) + (let ([bigger (if mutable? + (begin + (err/rt-test (dict-set smaller 1 val)) + (dict-set! smaller 1 val) + d) + (begin + (err/rt-test (dict-set! smaller 1 val)) + (dict-set smaller 1 val)))]) + (test cnt dict-count bigger) + (when (eq? val 'one) + (unless (pair? d) + (test #t equal? d bigger)))))]) + (try-add smaller "ONE") + (try-add d "ONE") + (try-add d 'one))))) + +(try-simple (vector 'zero 'one 'two) #t #f #f) +(try-simple #hash((1 . one) (#f . 7)) #f #t #t) +(try-simple #hasheq((1 . one) (#f . 7)) #f #t #t) +(try-simple (hash-copy #hash((1 . one) (#f . 7))) #t #t #f) +(try-simple (hash-copy #hasheq((1 . one) (#f . 7))) #t #t #f) +(try-simple '((0 . zero) (1 . one)) #f #t #t) +(try-simple '((1 . one) (0 . zero)) #f #t #t) +(try-simple (let ([h (make-custom-hash (lambda (a b) + (string=? (format "~a" a) + (format "~a" b))) + (lambda (a) + (equal-hash-code (format "~a" a))))]) + (dict-set! h "1" 'one) + (dict-set! h "2" 'two) + h) + #t #t #f + "1") +(try-simple (let* ([h (make-immutable-custom-hash + (lambda (a b) + (string=? (format "~a" a) + (format "~a" b))) + (lambda (a) + (equal-hash-code (format "~a" a))))] + [h (dict-set h "1" 'one)] + [h (dict-set h "2" 'two)]) + h) + #f #t #t + "1") + +;; ---------------------------------------- + +(report-errs) diff --git a/src/mzscheme/src/list.c b/src/mzscheme/src/list.c index a6d6b94660..8fd120d46c 100644 --- a/src/mzscheme/src/list.c +++ b/src/mzscheme/src/list.c @@ -1674,7 +1674,7 @@ static Scheme_Object *hash_table_put(int argc, Scheme_Object *argv[]) Scheme_Object *v = argv[0]; if (!SCHEME_HASHTRP(v)) { - scheme_wrong_type("hash-set", "immutable table", 0, argc, argv); + scheme_wrong_type("hash-set", "immutable hash", 0, argc, argv); return NULL; } @@ -1750,7 +1750,7 @@ static Scheme_Object *hash_table_remove_bang(int argc, Scheme_Object *argv[]) static Scheme_Object *hash_table_remove(int argc, Scheme_Object *argv[]) { if (!SCHEME_HASHTRP(argv[0])) - scheme_wrong_type("hash-remove", "immutable table", 0, argc, argv); + scheme_wrong_type("hash-remove", "immutable hash", 0, argc, argv); return (Scheme_Object *)scheme_hash_tree_set((Scheme_Hash_Tree *)argv[0], argv[1], NULL); } @@ -2173,7 +2173,7 @@ static Scheme_Object *do_make_hash_placeholder(const char *who, int eq, int argc ph = scheme_alloc_object(); ph->type = scheme_table_placeholder_type; - SCHEME_IPTR_VAL(ph) = l; + SCHEME_IPTR_VAL(ph) = argv[0]; SCHEME_PINT_VAL(ph) = eq; return ph;