diff --git a/collects/mzlib/for.ss b/collects/mzlib/for.ss index 1abd0a4088..407edf767b 100644 --- a/collects/mzlib/for.ss +++ b/collects/mzlib/for.ss @@ -1,33 +1,37 @@ (module for mzscheme - (provide fold-for fold-for-values fold-for* fold-for*-values + (provide for/fold for/fold-values for*/fold for*/fold-values for for-values for* for*-values - list-for list-for-values list-for* list-for*-values - lists-for lists-for-values lists-for* lists-for*-values - and-for and-for-values and-for* and-for*-values - or-for or-for-values or-for* or-for*-values - first-for first-for-values first-for* first-for*-values - last-for last-for-values last-for* last-for*-values + for/list for/list-values for*/list for*/list-values + for/lists for/lists-values for*/lists for*/lists-values + for/and for/and-values for*/and for*/and-values + for/or for/or-values for*/or for*/or-values + for/first for/first-values for*/first for*/first-values + for/last for/last-values for*/last for*/last-values (rename *in-range in-range) - (rename *in-nats in-nats) + (rename *in-naturals in-naturals) (rename *in-list in-list) (rename *in-vector in-vector) (rename *in-string in-string) (rename *in-bytes in-bytes) in-input-port-bytes in-input-port-chars + in-hash-table + in-hash-table-keys + in-hash-table-values + in-hash-table-pairs in-parallel stop-before stop-after (rename *in-indexed in-indexed) - sequence-run + sequence-generator define-sequence-syntax make-do-sequence - :do-gen) + :do-in) ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; sequence transformers: @@ -44,9 +48,9 @@ 0)) (define (create-sequence-transformer proc1 proc2 cert) - (unless (if (identifier? proc1) + (unless (or (identifier? proc1) (and (procedure? proc1) - (procedure-arity-includes? proc1 1)) + (procedure-arity-includes? proc1 1))) (raise-type-error 'define-sequence-syntax "identifier of procedure (arity 1)" 0 @@ -67,18 +71,18 @@ stx))) proc1) proc2 - cert))) + cert)) (define (certify-clause clause certifier introducer) - ;; This is slightly painful. The painsion into `:do-gen' involves a lot of pieces + ;; This is slightly painful. The painsion into `:do-in' involves a lot of pieces ;; that are no treated as sub-expressions. We have to push the certificates ;; down to all the relevant identifiers and expressions: (define (cert s) (certifier s #f introducer)) (define (map-cert s) (map (lambda (s) (certifier s #f #;introducer)) (syntax->list s))) - (syntax-case clause (:do-gen) - [[(id ...) (:do-gen ([(outer-id ...) outer-expr] ...) + (syntax-case clause (:do-in) + [[(id ...) (:do-in ([(outer-id ...) outer-expr] ...) outer-check ([loop-id loop-expr] ...) pos-guard @@ -99,7 +103,7 @@ [pre-guard (cert #'pre-guard)] [post-guard (cert #'post-guard)] [(loop-arg ...) (map-cert #'(loop-arg ...))]) - #`[(id ...) (:do-gen ([(outer-id ...) outer-expr] ...) + #`[(id ...) (:do-in ([(outer-id ...) outer-expr] ...) outer-check ([loop-id loop-expr] ...) pos-guard @@ -115,7 +119,7 @@ (define (expand-clause orig-stx clause) (let eloop ([use-transformer? #t]) - (syntax-case clause (values in-parallel stop-before stop-after :do-gen) + (syntax-case clause (values in-parallel stop-before stop-after :do-in) [[(id ...) rhs] (let ([ids (syntax->list #'(id ...))]) (for-each (lambda (id) @@ -149,7 +153,7 @@ certifier introducer)) (eloop #f)))))] - [[(id ...) (:do-gen . body)] + [[(id ...) (:do-in . body)] (syntax-case #'body () [(([(outer-id ...) outer-rhs] ...) outer-check @@ -159,7 +163,7 @@ pre-guard post-guard (loop-arg ...)) #'body] - [else (raise-syntax-error #f "bad :do-gen clause" orig-stx clause)])] + [else (raise-syntax-error #f "bad :do-in clause" orig-stx clause)])] [[(id) (values rhs)] (expand-clause orig-stx #'[(id) rhs])] [[(id ...) (in-parallel rhs ...)] @@ -243,7 +247,7 @@ orig-stx clause)])))) - (define-syntax (:do-gen stx) + (define-syntax (:do-in stx) (raise-syntax-error #f "illegal outside of a loop or comprehension binding" stx)) ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -284,7 +288,8 @@ (vector? v) (string? v) (bytes? v) - (input-port? v))) + (input-port? v) + (hash-table? v))) (define (make-sequence who v) (cond @@ -294,6 +299,7 @@ [(string? v) (:string-gen v)] [(bytes? v) (:bytes-gen v)] [(input-port? v) (:input-port-gen v)] + [(hash-table? v) (:hash-table-gen v cons (lambda (p) (values (car p) (cdr p))))] [else (raise (make-exn:fail:contract (format "for: expected a sequence for ~a, got something else: ~v" @@ -322,17 +328,26 @@ (lambda (x) #t) (lambda (x y) #t))))])) - (define (in-nats) - (make-do-sequence (lambda () - (values values - add1 - 0 - (lambda (x) #t) - (lambda (x) #t) - (lambda (x y) #t))))) + (define in-naturals + (case-lambda + [() (in-naturals 0)] + [(n) + (unless (and (integer? n) + (exact? n) + (n . >= . 0)) + (raise-type-error 'in-naturals + "exact non-negative integer" + n)) + (make-do-sequence (lambda () + (values values + add1 + n + (lambda (x) #t) + (lambda (x) #t) + (lambda (x y) #t))))])) (define (in-list l) - (unless (list? l) (raise-type-error 'in-list "list" l)) + ; (unless (list? l) (raise-type-error 'in-list "list" l)) (make-do-sequence (lambda () (:list-gen l)))) (define (:list-gen l) @@ -402,6 +417,23 @@ (lambda (x) (not (eof-object? x))) (lambda (x v) #t))))) + (define (in-hash-table ht) + (unless (hash-table? ht) (raise-type-error 'in-hash-table "hash-table" ht)) + (make-do-sequence (lambda () (:hash-table-gen ht cons (lambda (p) (values (car p) (cdr p))))))) + (define (in-hash-table-keys ht) + (unless (hash-table? ht) (raise-type-error 'in-hash-table-keys "hash-table" ht)) + (make-do-sequence (lambda () (:hash-table-gen ht (lambda (k v) k) values)))) + (define (in-hash-table-values ht) + (unless (hash-table? ht) (raise-type-error 'in-hash-table-values "hash-table" ht)) + (make-do-sequence (lambda () (:hash-table-gen ht (lambda (k v) v) values)))) + (define (in-hash-table-pairs ht) + (unless (hash-table? ht) (raise-type-error 'in-hash-table-values "hash-table" ht)) + (make-do-sequence (lambda () (:hash-table-gen ht cons values)))) + + (define (:hash-table-gen ht sel f) + (let ([l (hash-table-map ht sel)]) + (values (lambda (l) (f (car l))) cdr l pair? (lambda args #t) (lambda args #t)))) + (define (stop-before g pred) (unless (sequence? g) (raise-type-error 'stop-before "sequence" g)) (unless (and (procedure? pred) @@ -464,7 +496,7 @@ (make-do-sequence (lambda () (let-values ([(pos->vals pos-nexts inits pos-cont?s pre-cont?s post-cont?s) - (lists-for (p->v p-s i ps? pr? po?) ([g sequences]) + (for/lists (p->v p-s i ps? pr? po?) ([g sequences]) (make-sequence #f g))]) (values (lambda (poses) (apply values (map (lambda (pos->val pos) (pos->val pos)) @@ -488,9 +520,9 @@ ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; runnign sequences outside of a loop: - (define (sequence-run g) + (define (sequence-generator g) (unless (sequence? g) - (raise-type-error 'sequence-run "sequence" g)) + (raise-type-error 'sequence-generator "sequence" g)) (let-values ([(pos->val pos-next init pos-cont? pre-cont? post-cont?) (make-sequence #f g)]) (let ([pos init]) @@ -545,21 +577,21 @@ sequence-next))))))) ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - ;; core `fold-for' syntax + ;; core `for/fold' syntax (define-syntax values* (syntax-rules () [(_ x) x] [(_ x ...) (values x ...)])) - (define-syntax (fold-forX/derived stx) + (define-syntax (for/foldX/derived stx) (syntax-case stx () ;; Done case (no more clauses, and no generated clauses to emit): [(_ [orig-stx multi? first-multi? nested? emit? ()] ([fold-var fold-init] ...) () expr1 expr ...) #`(let ([fold-var fold-init] ...) (let () expr1 expr ...))] ;; Switch-to-emit case (no more clauses to generate): [(_ [orig-stx multi? first-multi? nested? #f binds] ([fold-var fold-init] ...) () . body) - #`(fold-forX/derived [orig-stx multi? first-multi? nested? #t binds] ([fold-var fold-init] ...) () . body)] + #`(for/foldX/derived [orig-stx multi? first-multi? nested? #t binds] ([fold-var fold-init] ...) () . body)] ;; Emit case: [(_ [orig-stx multi? first-multi? nested? #t binds] ([fold-var fold-init] ...) rest . body) (with-syntax ([(([outer-binding ...] @@ -578,7 +610,7 @@ (let-values (inner-binding ... ...) (if (and pre-guard ...) (let-values ([(fold-var ...) - (fold-forX/derived [orig-stx multi? first-multi? nested? #f ()] ([fold-var fold-var] ...) rest . body)]) + (for/foldX/derived [orig-stx multi? first-multi? nested? #f ()] ([fold-var fold-var] ...) rest . body)]) (if (and post-guard ...) (comp-loop fold-var ... loop-arg ... ...) (values* fold-var ...))) @@ -598,7 +630,7 @@ ;; Guard case, no pending emits: [(_ [orig-stx multi? first-multi? nested? #f ()] ([fold-var fold-init] ...) (#:when expr . rest) . body) #'(if expr - (fold-forX/derived [orig-stx multi? first-multi? nested? #f ()] ([fold-var fold-init] ...) rest . body) + (for/foldX/derived [orig-stx multi? first-multi? nested? #f ()] ([fold-var fold-init] ...) rest . body) (values* fold-init ...))] ;; Guard case, pending emits need to be flushed first [(_ [orig-stx multi? first-multi? nested? #f binds] ([fold-var fold-init] ...) (#:when expr . rest) . body) @@ -606,7 +638,7 @@ ;; Convert single-value form to multi-value form: [(_ [orig-stx #f #f nested? #f binds] fold-bind ([id rhs] . rest) . body) (identifier? #'id) - #'(fold-forX/derived [orig-stx #f #t nested? #f binds] fold-bind ([(id) rhs] . rest) . body)] + #'(for/foldX/derived [orig-stx #f #t nested? #f binds] fold-bind ([(id) rhs] . rest) . body)] ;; If we get here in single-value mode, then it's a bad clause: [(_ [orig-stx #f #f nested? #f binds] fold-bind (clause . rest) . body) (raise-syntax-error @@ -621,25 +653,25 @@ [(_ [orig-stx . _rest] . _rest2) (raise-syntax-error #f "bad syntax" #'orig-stx)])) - (define-syntax fold-for/derived + (define-syntax for/fold/derived (syntax-rules () [(_ orig-stx . rest) - (fold-forX/derived [orig-stx #f #f #f #f ()] . rest)])) + (for/foldX/derived [orig-stx #f #f #f #f ()] . rest)])) - (define-syntax fold-for-values/derived + (define-syntax for/fold-values/derived (syntax-rules () [(_ orig-stx . rest) - (fold-forX/derived [orig-stx #t #t #f #f ()] . rest)])) + (for/foldX/derived [orig-stx #t #t #f #f ()] . rest)])) - (define-syntax fold-for*/derived + (define-syntax for*/fold/derived (syntax-rules () [(_ orig-stx . rest) - (fold-forX/derived [orig-stx #f #f #t #f ()] . rest)])) + (for/foldX/derived [orig-stx #f #f #t #f ()] . rest)])) - (define-syntax fold-for*-values/derived + (define-syntax for*/fold-values/derived (syntax-rules () [(_ orig-stx . rest) - (fold-forX/derived [orig-stx #t #t #t #f ()] . rest)])) + (for/foldX/derived [orig-stx #t #t #t #f ()] . rest)])) ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; derived `for' syntax @@ -684,23 +716,23 @@ (syntax-rules () [(_ (for for-values for* for*-values) fold-bind wrap rhs-wrap combine) (begin - (define-syntax-via-derived for fold-for/derived fold-bind wrap rhs-wrap combine #f) - (define-syntax-via-derived for-values fold-for-values/derived fold-bind wrap rhs-wrap combine #t) - (define-syntax-via-derived for* fold-for*/derived fold-bind wrap rhs-wrap combine #f) - (define-syntax-via-derived for*-values fold-for*-values/derived fold-bind wrap rhs-wrap combine #t))])) + (define-syntax-via-derived for for/fold/derived fold-bind wrap rhs-wrap combine #f) + (define-syntax-via-derived for-values for/fold-values/derived fold-bind wrap rhs-wrap combine #t) + (define-syntax-via-derived for* for*/fold/derived fold-bind wrap rhs-wrap combine #f) + (define-syntax-via-derived for*-values for*/fold-values/derived fold-bind wrap rhs-wrap combine #t))])) - (define-syntax (fold-for stx) + (define-syntax (for/fold stx) (syntax-case stx () - [(_ . rest) (quasisyntax/loc stx (fold-for/derived #,stx . rest))])) - (define-syntax (fold-for-values stx) + [(_ . rest) (quasisyntax/loc stx (for/fold/derived #,stx . rest))])) + (define-syntax (for/fold-values stx) (syntax-case stx () - [(_ . rest) (quasisyntax/loc stx (fold-for-values/derived #,stx . rest))])) - (define-syntax (fold-for* stx) + [(_ . rest) (quasisyntax/loc stx (for/fold-values/derived #,stx . rest))])) + (define-syntax (for*/fold stx) (syntax-case stx () - [(_ . rest) (quasisyntax/loc stx (fold-for*/derived #,stx . rest))])) - (define-syntax (fold-for*-values stx) + [(_ . rest) (quasisyntax/loc stx (for*/fold/derived #,stx . rest))])) + (define-syntax (for*/fold-values stx) (syntax-case stx () - [(_ . rest) (quasisyntax/loc stx (fold-for*-values/derived #,stx . rest))])) + [(_ . rest) (quasisyntax/loc stx (for*/fold-values/derived #,stx . rest))])) (define-for-variants (for for-values for* for*-values) ([fold-var (void)]) @@ -708,13 +740,13 @@ (lambda (x) x) (lambda (x) `(,#'begin ,x ,#'(void)))) - (define-for-variants (list-for list-for-values list-for* list-for*-values) + (define-for-variants (for/list for/list-values for*/list for*/list-values) ([fold-var null]) (lambda (x) `(,#'reverse ,x)) (lambda (x) x) (lambda (x) `(,#'cons ,x ,#'fold-var))) - (define-for-syntax (make-lists-for-values fold-for-id) + (define-for-syntax (make-for/lists-values for/fold-id) (lambda (stx) (syntax-case stx () [(_ (id ...) bindings expr1 expr ...) @@ -727,40 +759,40 @@ id))) ids) (with-syntax ([(id2 ...) (generate-temporaries ids)] - [fold-for fold-for-id] + [for/fold for/fold-id] [orig-stx stx]) #'(let-values ([(id ...) - (fold-for orig-stx ([id null] ...) bindings + (for/fold orig-stx ([id null] ...) bindings (let-values ([(id2 ...) (let () expr1 expr ...)]) (values* (cons id2 id) ...)))]) (values* (reverse id) ...))))]))) - (define-syntax lists-for (make-lists-for-values #'fold-for/derived)) - (define-syntax lists-for-values (make-lists-for-values #'fold-for-values/derived)) - (define-syntax lists-for* (make-lists-for-values #'fold-for*/derived)) - (define-syntax lists-for*-values (make-lists-for-values #'fold-for*-values/derived)) + (define-syntax for/lists (make-for/lists-values #'for/fold/derived)) + (define-syntax for/lists-values (make-for/lists-values #'for/fold-values/derived)) + (define-syntax for*/lists (make-for/lists-values #'for*/fold/derived)) + (define-syntax for*/lists-values (make-for/lists-values #'for*/fold-values/derived)) - (define-for-variants (and-for and-for-values and-for* and-for*-values) + (define-for-variants (for/and for/and-values for*/and for*/and-values) ([result #t]) (lambda (x) x) (lambda (rhs) #`(stop-after #,rhs (lambda x (not result)))) (lambda (x) x)) - (define-for-variants (or-for or-for-values or-for* or-for*-values) + (define-for-variants (for/or for/or-values for*/or for*/or-values) ([result #f]) (lambda (x) x) (lambda (rhs) #`(stop-after #,rhs (lambda x result))) (lambda (x) x)) - (define-for-variants (first-for first-for-values first-for* first-for*-values) + (define-for-variants (for/first for/first-values for*/first for*/first-values) ([val #f][stop? #f]) (lambda (x) #`(let-values ([(val _) #,x]) val)) (lambda (rhs) #`(stop-after #,rhs (lambda x stop?))) (lambda (x) #`(values #,x #t))) - (define-for-variants (last-for last-for-values last-for* last-for*-values) + (define-for-variants (for/last for/last-values for*/last for*/last-values) ([result #f]) (lambda (x) x) (lambda (rhs) rhs) @@ -775,7 +807,7 @@ (let loop ([stx stx]) (syntax-case stx () [[(id) (_ a b step)] #`[(id) - (:do-gen + (:do-in ;; outer bindings: ([(start) a] [(end) b] [(inc) step]) ;; outer check: @@ -804,29 +836,36 @@ [[(id) (_ b)] (loop #'[(id) (_ 0 b 1)])] [_else #f])))) - (define-sequence-syntax *in-nats - #'in-nats + (define-sequence-syntax *in-naturals + #'in-naturals (lambda (orig-stx stx) - (syntax-case stx () - [[(id) (_)] #`[(id) - (:do-gen - ;; outer bindings: - () - ;; outer check: - (void) - ;; loop bindings: - ([pos 0]) - ;; pos check - #t - ;; inner bindings - ([(id) pos]) - ;; pre guard - #t - ;; post guard - #t - ;; loop args - ((+ pos 1)))]] - [_else #f]))) + (let loop ([stx stx]) + (syntax-case stx () + [[(id) (_ start)] + (and (integer? (syntax-e #'start)) + (exact? (syntax-e #'start)) + ((syntax-e #'start) . >= . 0)) + #`[(id) + (:do-in + ;; outer bindings: + () + ;; outer check: + (void) + ;; loop bindings: + ([pos start]) + ;; pos check + #t + ;; inner bindings + ([(id) pos]) + ;; pre guard + #t + ;; post guard + #t + ;; loop args + ((+ pos 1)))]] + [[(id) (_)] + (loop #'[(id) (_ 0)])] + [_else #f])))) (define-sequence-syntax *in-list #'in-list @@ -834,15 +873,15 @@ (syntax-case stx () [((id) (_ lst-expr)) #'[(id) - (:do-gen + (:do-in ;;outer bindings ([(lst) lst-expr]) ;; outer check - (unless (list? lst) (in-list lst)) + (void) ; (unless (list? lst) (in-list lst)) ;; loop bindings ([lst lst]) ;; pos check - (pair? lst) + (not (null? lst)) ;; inner bindings ([(id) (car lst)]) ;; pre guard @@ -865,7 +904,7 @@ (syntax-case stx () [((id) (_ vec-expr)) #'[(id) - (:do-gen + (:do-in ;;outer bindings ([(vec len) (let ([vec vec-expr]) (unless (vector? vec) @@ -913,4 +952,4 @@ (lambda (orig-stx stx) (syntax-case stx () [((id1 id2) (_ gen-expr)) - #'[(id1 id2) (in-parallel gen-expr (*in-nats))]])))) + #'[(id1 id2) (in-parallel gen-expr (*in-naturals))]])))) diff --git a/collects/scribble/eval.ss b/collects/scribble/eval.ss index d4af007dcb..470945a8c9 100644 --- a/collects/scribble/eval.ss +++ b/collects/scribble/eval.ss @@ -46,16 +46,39 @@ (if (flow? p) p (make-flow (list p)))))) - (append - (if (string? (car val-list+outputs)) - (map - (lambda (s) - (list (make-flow (list (make-paragraph - (list - (hspace 2) - (span-class "schemeerror" - (italic s)))))))) - (let sloop ([s (car val-list+outputs)]) + (if (string=? "" (cdar val-list+outputs)) + null + (list + (list + (make-flow + (list + (let ([s (regexp-split #rx"\n" + (regexp-replace #rx"\n$" + (cdar val-list+outputs) + ""))]) + (if (= 1 (length s)) + (make-paragraph + (list + (hspace 2) + (span-class "schemestdout" (car s)))) + (make-table + #f + (map (lambda (s) + (list (make-flow (list (make-paragraph + (list + (hspace 2) + (span-class "schemestdout" s))))))) + s))))))))) + (if (string? (caar val-list+outputs)) + ;; Error result case: + (map + (lambda (s) + (list (make-flow (list (make-paragraph + (list + (hspace 2) + (span-class "schemeerror" + (italic s)))))))) + (let sloop ([s (caar val-list+outputs)]) (if ((string-length s) . > . maxlen) ;; break the error message into multiple lines: (let loop ([pos (sub1 maxlen)]) @@ -67,43 +90,20 @@ (sloop (substring s (add1 pos))))] [else (loop (sub1 pos))])) (list s)))) - (append - (if (string=? "" (cdar val-list+outputs)) + ;; Normal result case: + (let ([val-list (caar val-list+outputs)]) + (if (equal? val-list (list (void))) null - (list - (list - (make-flow - (list - (let ([s (regexp-split #rx"\n" - (regexp-replace #rx"\n$" - (cdar val-list+outputs) - ""))]) - (if (= 1 (length s)) - (make-paragraph - (list - (hspace 2) - (span-class "schemestdout" (car s)))) - (make-table - #f - (map (lambda (s) - (list (make-flow (list (make-paragraph - (list - (hspace 2) - (span-class "schemestdout" s))))))) - s))))))))) - (let ([val-list (caar val-list+outputs)]) - (if (equal? val-list (list (void))) - null - (map (lambda (v) - (list (make-flow (list (make-paragraph - (list - (hspace 2) - (span-class "schemeresult" - (to-element/no-color v)))))))) - val-list))))) - (loop (cdr expr-paras) - (cdr val-list+outputs) - #f)))))))) + (map (lambda (v) + (list (make-flow (list (make-paragraph + (list + (hspace 2) + (span-class "schemeresult" + (to-element/no-color v)))))))) + val-list)))) + (loop (cdr expr-paras) + (cdr val-list+outputs) + #f))))))) (define (do-eval s) (cond @@ -121,7 +121,8 @@ (let ([o (open-output-string)]) (parameterize ([current-output-port o]) (with-handlers ([exn? (lambda (e) - (exn-message e))]) + (cons (exn-message e) + (get-output-string o)))]) (cons (let ([v (do-plain-eval s #t)]) (copy-value v (make-hash-table))) (get-output-string o)))))])) diff --git a/collects/scribble/manual.ss b/collects/scribble/manual.ss index 9781e0f2fc..9a3eaf6bcb 100644 --- a/collects/scribble/manual.ss +++ b/collects/scribble/manual.ss @@ -43,10 +43,16 @@ (define (to-element/id s) (make-element "schemesymbol" (list (to-element/no-color s)))) - (define-code scheme to-element unsyntax (lambda (ctx s v) s)) - (define-code schemeresult to-element/result unsyntax (lambda (ctx s v) s)) - (define-code schemeid to-element/id unsyntax (lambda (ctx s v) s)) - (define-code schememodname to-element unsyntax (lambda (ctx s v) s)) + (define (keep-s-expr ctx s v) s) + (define (add-sq-prop s name val) + (if (eq? name 'paren-shape) + (make-shaped-parens s val) + s)) + + (define-code scheme to-element unsyntax keep-s-expr add-sq-prop) + (define-code schemeresult to-element/result unsyntax keep-s-expr add-sq-prop) + (define-code schemeid to-element/id unsyntax keep-s-expr add-sq-prop) + (define-code schememodname to-element unsyntax keep-s-expr add-sq-prop) (define (litchar . strs) (unless (andmap string? strs) diff --git a/collects/scribble/scheme.ss b/collects/scribble/scheme.ss index 68efc3ce29..6e5590e6fd 100644 --- a/collects/scribble/scheme.ss +++ b/collects/scribble/scheme.ss @@ -13,7 +13,9 @@ syntax-ize syntax-ize-hook current-keyword-list - current-variable-list) + current-variable-list + + (struct shaped-parens (val shape))) (define no-color "schemeplain") (define meta-color "schemeplain") @@ -26,12 +28,13 @@ (define opt-color "schemeopt") (define current-keyword-list - (make-parameter '(define let let* letrec require provide + (make-parameter '(define let let* letrec require provide let-values lambda new send if cond begin else and or define-syntax syntax-rules define-struct quote quasiquote unquote unquote-splicing syntax quasisyntax unsyntax unsyntax-splicing - fold-for list-for list-for* for))) + for/fold for/list for*/list for for/and for/or for* for*/or for*/and for*/fold + for-values for*/list-values for/first for/last))) (define current-variable-list (make-parameter null)) @@ -301,6 +304,7 @@ (string? (syntax-e c)) (bytes? (syntax-e c)) (char? (syntax-e c)) + (keyword? (syntax-e c)) (boolean? (syntax-e c))) value-color] [(identifier? c) @@ -336,13 +340,13 @@ (define-syntax (define-code stx) (syntax-case stx () - [(_ code typeset-code uncode d->s) + [(_ code typeset-code uncode d->s stx-prop) (syntax/loc stx (define-syntax (code stx) (define (stx->loc-s-expr v) (cond [(syntax? v) - (let ([mk `(d->s + (let ([mk `(,#'d->s #f ,(syntax-case v (uncode) [(uncode e) #'e] @@ -354,7 +358,7 @@ ,(syntax-span v)))]) (let ([prop (syntax-property v 'paren-shape)]) (if prop - `(syntax-property ,mk 'paren-shape ,prop) + `(,#'stx-prop ,mk 'paren-shape ,prop) mk)))] [(pair? v) `(cons ,(stx->loc-s-expr (car v)) ,(stx->loc-s-expr (cdr v)))] @@ -365,13 +369,13 @@ [(null? v) 'null] [else `(quote ,v)])) (define (cvt s) - (d->s #'here (stx->loc-s-expr s) #f)) + (datum->syntax-object #'here (stx->loc-s-expr s) #f)) (syntax-case stx () [(_ expr) #`(typeset-code #,(cvt #'expr))] [(_ expr (... ...)) #`(typeset-code #,(cvt #'(code:line expr (... ...))))])))] [(_ code typeset-code uncode) - #'(define-code code typeset-code uncode datum->syntax-object)] + #'(define-code code typeset-code uncode datum->syntax-object syntax-property)] [(_ code typeset-code) #'(define-code code typeset-code unsyntax)])) @@ -406,10 +410,16 @@ (loop (cons (car r) r) (sub1 i))))) l)))) + (define-struct shaped-parens (val shape)) + (define (syntax-ize v col) (cond [((syntax-ize-hook) v col) => (lambda (r) r)] + [(shaped-parens? v) + (syntax-property (syntax-ize (shaped-parens-val v) col) + 'paren-shape + (shaped-parens-shape v))] [(and (list? v) (pair? v) (memq (car v) '(quote unquote unquote-splicing))) diff --git a/collects/scribblings/guide/data.scrbl b/collects/scribblings/guide/data.scrbl index 9d1d8d0203..8ef2f3e9d8 100644 --- a/collects/scribblings/guide/data.scrbl +++ b/collects/scribblings/guide/data.scrbl @@ -20,7 +20,8 @@ built-in datatypes for simple forms of data. @include-section["symbols.scrbl"] @include-section["pairs.scrbl"] @include-section["vectors.scrbl"] -@include-section["hash-tables.scrbl"] -@include-section["keywords.scrbl"] @include-section["boxes.scrbl"] +@include-section["hash-tables.scrbl"] +@include-section["paths.scrbl"] +@include-section["keywords.scrbl"] @include-section["void-and-undef.scrbl"] diff --git a/collects/scribblings/guide/for.scrbl b/collects/scribblings/guide/for.scrbl new file mode 100644 index 0000000000..a18adff506 --- /dev/null +++ b/collects/scribblings/guide/for.scrbl @@ -0,0 +1,427 @@ +#reader(lib "docreader.ss" "scribble") +@require[(lib "manual.ss" "scribble")] +@require[(lib "eval.ss" "scribble")] +@require["guide-utils.ss"] + +@interaction-eval[(require (lib "for.ss"))] + +@title[#:tag "for"]{Iterations and Comprehensions} + +The @scheme[for] family of syntactic forms support iteration over +@defterm{sequences}. Lists, vectors, strings, byte strings, input +ports, and hash tables can all be used as sequences, and constructors +like @scheme[in-range] offer even more kinds of sequences. + +Variants of @scheme[for] accumulate iteration results in different +ways, but they all have the same syntactic shape. Simplifying, for +now, the syntax of @scheme[for] is + +@schemeblock[ +(for ([_id _sequence-expr]) + _body-expr) +] + +A @scheme[for] loop iterates through the sequence produced by the +@scheme[_sequence-expr]. For each element of the sequence, +@scheme[for] binds the element to @scheme[_id], and then it evaluates +the @scheme[_body-expr] for side effects. + +@examples[ +(for ([i '(1 2 3)]) + (display i)) +(for ([i "abc"]) + (printf "~a..." i)) +] + +The @scheme[for/list] variant of @scheme[for] is more Scheme-like. It +accumulates @scheme[_body-expr] results into a list, instead of +evaluating @scheme[_body-expr] only for side effects. In more +technical terms, @scheme[for/list] implements a @defterm{list +comprehension}. + +@examples[ +(for/list ([i '(1 2 3)]) + (* i i)) +(for/list ([i "abc"]) + i) +] + +The full syntax of @scheme[for] accomodates multiple sequences to +iterate in parallel, and the @scheme[for*] variant nests the +iterations instead of running them in parallel. More variants of +@scheme[for] and @scheme[for*] accumulate @scheme[_body-expr] results +in different ways. In all of these variants, predicates that prune +iterations can be included along with bindings. + +Before details on the variations of @scheme[for], though, it's best to +see the kinds of sequence generators that make interesting examples. + +@section{Sequence Constructors} + +The @scheme[in-range] procedure generates a sequence of numbers, given +an optional starting number (which defaults to @scheme[0]), a number +before which the sequences ends, and an optional step (which defaults +to @scheme[1]). + +@examples[ +(for ([i (in-range 3)]) + (display i)) +(for ([i (in-range 1 4)]) + (display i)) +(for ([i (in-range 1 4 2)]) + (display i)) +(for ([i (in-range 4 1 -1)]) + (display i)) +(for ([i (in-range 1 4 1/2)]) + (printf " ~a " i)) +] + +The @scheme[in-naturals] procedure is similar, except that the +starting number must be an exact non-negative integer (which defaults +to @scheme[0]), the step is always @scheme[1], and there is no upper +limit. A @scheme[for] loop using just @scheme[in-naturals] will never +terminate. + +@examples[ +(for ([i (in-naturals)]) + (if (= i 10) + (error "too much!") + (display i))) +] + +The @scheme[stop-before] and @scheme[stop-after] procedures construct +a new sequence given a sequence and a predicate. The new sequence is +like the given sequence, but truncated either immediately before or +immediately after the first element for which the predicate returns +true. + +@examples[ +(for ([i (stop-before "abc def" + char-whitespace?)]) + (display i)) +] + +Sequence constructors like @scheme[in-list], @scheme[in-vector] and +@scheme[in-string] simply make explicit the use of a list, vector, or +string as a sequence. Since they raise an exception when given the +wrong kind of value and otherwise avoid a run-time dispatch to +determine the sequence type, they enable more efficient code +generation; see @secref["for-performance"] for more information. + +@examples[ +(for ([i (in-string "abc")]) + (display i)) +(for ([i (in-string '(1 2 3))]) + (display i)) +] + + +@section{@scheme[for] and @scheme[for*]} + +The full syntax of @scheme[for] is + +@schemeblock[ +(for (_clause ...) + _body-expr ...+) +code:blank +#, @elem{where} _clause #, @elem{is one of} + [_id _sequence-expr] + #:when _boolean-expr +] + +When multiple @scheme[[_id _sequence-expr]] clauses are provided +in a @scheme[for] form, the corresponding sequences are traversed in +parallel: + +@interaction[ +(for ([i (in-range 1 4)] + [chapter '("Intro" "Details" "Conclusion")]) + (printf "Chapter ~a. ~a\n" i chapter)) +] + +With parallel sequences, the @scheme[for] expression stops iterating +when any sequence ends. This behavior allows @scheme[in-naturals], +which creates an infinite sequence of numbers, to be used for +indexing: + +@interaction[ +(for ([i (in-naturals 1)] + [chapter '("Intro" "Details" "Conclusion")]) + (printf "Chapter ~a. ~a\n" i chapter)) +] + +The @scheme[for*] form, which has the same syntax as @scheme[for], +nests multiple sequences instead of running them in parallel: + +@interaction[ +(for* ([book '("Guide" "Reference")] + [chapter '("Intro" "Details" "Conclusion")]) + (printf "~a ~a\n" book chapter)) +] + +Thus, @scheme[for*] is a shorthand for nested @scheme[for]s in the +same way that @scheme[let*] is a shorthand for nested @scheme[let]s. + +The @scheme[#:when _boolean-expr] form of a @scheme[_clause] is +another shorthand. It allows the @scheme[_body-expr]s to evaluate only +when the @scheme[_boolean-expr] produces a true value: + +@interaction[ +(for* ([book '("Guide" "Reference")] + [chapter '("Intro" "Details" "Conclusion")] + #:when (not (equal? chapter "Details"))) + (printf "~a ~a\n" book chapter)) +] + +A @scheme[_boolean-expr] with @scheme[#:when] can refer to any of the +preceding iteration bindings. In a @scheme[for] form, this scoping +makes sense only if the test is nested in the interation of the +preceding bindings; thus, bindings separated by @scheme[#:when] are +mutually nested, instead of in parallel, even with @scheme[for]. + +@interaction[ +(for ([book '("Guide" "Reference" "Notes")] + #:when (not (equal? book "Notes")) + [i (in-naturals 1)] + [chapter '("Intro" "Details" "Conclusion" "Index")] + #:when (not (equal? chapter "Index"))) + (printf "~a Chapter ~a. ~a\n" book i chapter)) +] + +@section{@scheme[for/list] and @scheme[for*/list]} + +The @scheme[for/list] form, which has the same syntax as @scheme[for], +evaluates the @scheme[_body-expr]s to obtain values that go into a +newly constructed list: + +@interaction[ +(for/list ([i (in-naturals 1)] + [chapter '("Intro" "Details" "Conclusion")]) + (string-append (number->string i) ". " chapter)) +] + +A @scheme[#:when] clause in a @scheme[for-list] form prunes the result +list along with evaluations of the @scheme[_body-expr]s: + +@interaction[ +(for/list ([i (in-naturals 1)] + [chapter '("Intro" "Details" "Conclusion")] + #:when (odd? i)) + chapter) +] + +This pruning behavior of @scheme[#:when] is more useful with +@scheme[for/list] than @scheme[for]. Whereas a plain @scheme[when] +form normally suffices with @scheme[for], a @scheme[when] expression +form in a @scheme[for/list] would cause the result list to contain +@void-const[]s instead of omitting list elements. + +The @scheme[for*/list] is like @scheme[for*], nesting multiple +iterations: + +@interaction[ +(for*/list ([book '("Guide" "Ref.")] + [chapter '("Intro" "Details")]) + (string-append book " " chapter)) +] + +A @scheme[for*/list] form is not quite the same thing as nested +@scheme[for/list] forms. Nested @scheme[for/list]s would produce a +list of lists, instead of one flattened list. Much like +@scheme[#:when], then, the nesting of @scheme[for*/list] is more +useful than the nesting of @scheme[for*]. + +@section{@scheme[for/and] and @scheme[for/or]} + +The @scheme[for/and] form combines iteration results with +@scheme[and], stopping as soon as it encounters @scheme[#f]: + +@interaction[ +(for/and ([chapter '("Intro" "Details" "Conclusion")]) + (equal? chapter "Intro")) +] + +The @scheme[for/or] form combines iteration results with @scheme[or], +stopping as soon as it encounters a true value: + +@interaction[ +(for/or ([chapter '("Intro" "Details" "Conclusion")]) + (equal? chapter "Intro")) +] + +As usual, the @scheme[for*/and] and @scheme[for*/or] forms provide the +same facility with nested iterations. + +@section{@scheme[for/first] and @scheme[for/last]} + +The @scheme[for/first] form returns the result of the first time that +the @scheme[_body-expr]s are evaluated, skipping further iterations. +This form is most useful with a @scheme[#:when] clause. + +@interaction[ +(for/first ([chapter '("Intro" "Details" "Conclusion" "Index")] + #:when (not (equal? chapter "Intro"))) + chapter) +] + +If the @scheme[_body-expr]s are evaluated zero time, then the result +is @scheme[#f]. + +The @scheme[for/last] form runs all iterations, returning the value of +the last iteration (or @scheme[#f] if no iterations are run): + +@interaction[ +(for/last ([chapter '("Intro" "Details" "Conclusion" "Index")] + #:when (not (equal? chapter "Index"))) + chapter) +] + +As usual, the @scheme[for*/first] and @scheme[for*/last] forms provide +the same facility with nested iterations: + +@interaction[ +(for*/first ([book '("Guide" "Reference")] + [chapter '("Intro" "Details" "Conclusion" "Index")] + #:when (not (equal? chapter "Intro"))) + (list book chapter)) + +(for*/last ([book '("Guide" "Reference")] + [chapter '("Intro" "Details" "Conclusion" "Index")] + #:when (not (equal? chapter "Index"))) + (list book chapter)) +] + +@section{@scheme[for/fold] and @scheme[for*/fold]} + +The @scheme[for/fold] form generalizes the way to combine iteration +results. Its syntax is slightly different than the syntax of +@scheme[for], because accumulation variables must be declared at the +beginning: + +@schemeblock[ +(for/fold ([_accum-id _init-expr] ...) + (_clause ...) + _body-expr ...+) +] + +In the simple case, only one @scheme[[_accum-id _init-expr]] is +provided, and the result of the @scheme[for/fold] is the final value +for @scheme[_accum-id], which starts out with the value of +@scheme[_init-expr]. In the @scheme[_clause]s and +@scheme[_body-expr]s, @scheme[_accum-id] can be referenced to gets its +current value, and the last @scheme[_body-expr] provides the value of +@scheme[_accum-id] for the netx iteration. + +@examples[ +(for/fold ([len 0]) + ([chapter '("Intro" "Conclusion")]) + (+ len (string-length chapter))) +(for/fold ([prev #f]) + ([i (in-naturals 1)] + [chapter '("Intro" "Details" "Details" "Conclusion")] + #:when (not (equal? chapter prev))) + (printf "~a. ~a\n" i chapter) + chapter) +] + +When multiple @scheme[_accum-id]s are specified, then the last +@scheme[_body-expr] must produce multiple values, one for each +@scheme[_accum-id]. The @scheme[for/fold] expression itself produces +multiple values for the results. + +@examples[ +(for/fold ([prev #f] + [counter 1]) + ([chapter '("Intro" "Details" "Details" "Conclusion")] + #:when (not (equal? chapter prev))) + (printf "~a. ~a\n" counter chapter) + (values chapter + (add1 counter))) +] + +@section{Multiple-Valued Sequences} + +In the same way that a procedure or expression can produce multiple +values, individual iterations of a sequence can produce multiple +elements. For example, a hash table as a sequence generates two +values for each iteration: a key and a value. + +In the same way that @scheme[let-values] binds multiple results to +multiple identifiers, @scheme[for-values] binds multiple sequence +elements to multiple iteration identifiers: + +@interaction[ +(for-values ([(k v) #hash(("apple" . 1) ("banana" . 3))]) + (printf "~a count: ~a\n" k v)) +] + +A @schemekeywordfont{-values} variant exists for all @scheme[for] +variants. For example, @scheme[for*/list-values] nests iterations, +builds a list, and works with multiple-valued sequences: + +@interaction[ +(for*/list-values ([(k v) #hash(("apple" . 1) ("banana" . 3))] + [(i) (in-range v)]) + k) +] + + +@section[#:tag "for-performance"]{Iteration Performance} + +Ideally, a @scheme[for] iteration should run as fast as a loop that +your write by hand as a recursive-function invocation. A hand-written +loop, however, is normally specific to a particular kind of data, such +as lists. In that case, the hand-written loop uses @scheme[car] and +@scheme[cdr] directly, instead of handling all forms of sequences and +dispatching to an appropriate iterator. + +The @scheme[for] forms can provide the performance of hand-written +loops when enough information is apparent about the sequences to +iterate. Specifically, the clause should have one of the following +@scheme[_fast-clause] forms: + +@schemeblock[ +#, @elem{a} _fast-clause #, @elem{is one of} + [_id (in-range _expr)] + [_id (in-range _expr _expr)] + [_id (in-range _expr _expr _expr)] + [_id (in-naturals)] + [_id (in-naturals _expr)] + [_id (in-list _expr)] + [_id (in-vector _expr)] + [_id (in-string _expr)] + [_id (in-bytes _expr)] + [_id (stop-before _fast-clause _predicate-expr)] + [_id (stop-after _fast-clause _predicate-expr)] +] + +@examples[ +(time (for ([i (in-range 100000)]) + (for ([elem (in-list '(a b c d e f g h))]) (code:comment #, @elem{fast}) + (void)))) +(time (for ([i (in-range 100000)]) + (for ([elem '(a b c d e f g h)]) (code:comment #, @elem{slower}) + (void)))) +(time (let ([seq (in-list '(a b c d e f g h))]) + (for ([i (in-range 100000)]) + (for ([elem seq]) (code:comment #, @elem{slower}) + (void))))) +] + +In the case of @scheme[for-values] forms, a few more +@scheme[_fast-values-clause]s provide good performance, in addition to +the obvious variants of @scheme[_fast-clause] forms: + +@schemeblock[ +#, @elem{a} _fast-values-clause #, @elem{is one of} + [(_id) (in-range _expr)] + ... + [(_id _id) (in-indexed _fast-clause)] + [(_id ...) (in-parallel _fast-clause ...)] +] + +The grammars above are not complete, because the set of syntactic +patterns that provide good performance is extensible, just like the +set of sequence values. The documentation for a sequence constructor +should indicate the performance impliciations of using it directly in +a @scheme[for] @scheme[_clause]. diff --git a/collects/scribblings/guide/forms.scrbl b/collects/scribblings/guide/forms.scrbl new file mode 100644 index 0000000000..247355cce6 --- /dev/null +++ b/collects/scribblings/guide/forms.scrbl @@ -0,0 +1,14 @@ +#reader(lib "docreader.ss" "scribble") +@require[(lib "manual.ss" "scribble")] +@require[(lib "eval.ss" "scribble")] +@require["guide-utils.ss"] + +@title[#:tag "scheme-forms" #:style 'toc]{Programs and Expressions} + +@local-table-of-contents[] + +@include-section["module-basics.scrbl"] + +@include-section["for.scrbl"] + + diff --git a/collects/scribblings/guide/guide.scrbl b/collects/scribblings/guide/guide.scrbl index f2502ba06a..4c2776757c 100644 --- a/collects/scribblings/guide/guide.scrbl +++ b/collects/scribblings/guide/guide.scrbl @@ -26,12 +26,7 @@ precise details to @|MzScheme| and other reference manuals. @include-section["define-struct.scrbl"] -@; ---------------------------------------------------------------------- -@section[#:tag "scheme-forms"]{Programs and Expressions} - -@subsection{Module Basics} - -@subsection{Definition and Expression Forms} +@include-section["forms.scrbl"] @; ---------------------------------------------------------------------- @section[#:tag "contracts"]{Contracts} diff --git a/collects/scribblings/guide/keywords.scrbl b/collects/scribblings/guide/keywords.scrbl index 6186bd2c39..b98ae119bc 100644 --- a/collects/scribblings/guide/keywords.scrbl +++ b/collects/scribblings/guide/keywords.scrbl @@ -5,3 +5,27 @@ @title[#:tag "keywords"]{Keywords} +A @defterm{keyword} is similar to a symbol (see @secref["symbols"]), +but its printed form is prefixed with @schemefont{#:}. Unlike a +symbol, it's printed form is also its expression form. + +@refdetails["mz:parse-keyword"]{the syntax of keywords} + +@examples[ +(string->keyword "apple") +#:apple +(eq? #:apple (string->keyword "apple")) +] + +Although keywords are values, they are intented for use as special +markers in argument lists and in certain syntactic forms. + +@italic{Need some examples here, once we have more keyword-based +procedures and syntax in place...} + +Keywords should not be used simply as another kind of symbol. Use +symbols, instead of keywords, for run-time flags and enumerations. + +@examples[ +(code:line (bytes->path #"/usr/tmp" 'unix) (code:comment #, @t{@scheme['unix], not @scheme[#:unix]})) +] diff --git a/collects/scribblings/guide/lists.scrbl b/collects/scribblings/guide/lists.scrbl index 1656c42c27..75e7ea2865 100644 --- a/collects/scribblings/guide/lists.scrbl +++ b/collects/scribblings/guide/lists.scrbl @@ -108,15 +108,15 @@ procedures. One reason is that @scheme[map], @scheme[ormap], list loops. @;------------------------------------------------------------------------ -@section{Iterative Folds and Comprehensions: @scheme[fold-for] and @scheme[list-for]} +@section{Iterative Folds and Comprehensions: @scheme[for/fold] and @scheme[for/list]} Besides iteration procedures like @scheme[foldl], Scheme provides a syntactic form for iteration that more closely resembles the syntax of other languages. The @scheme[foldl] example above can be written with -the @scheme[fold-for] syntax as follows: +the @scheme[for/fold] syntax as follows: @interaction[ -(fold-for ([sum 0]) +(for/fold ([sum 0]) ([elem (list 1 2 3)]) (+ sum (* elem elem))) ] @@ -136,52 +136,52 @@ EOS The only significant difference is that the updating of @scheme[sum] and the return of @scheme[sum]'s value are implicit. Those implicit -actions are why the form is called @scheme[fold-for] instead of just +actions are why the form is called @scheme[for/fold] instead of just @scheme[for]. -Along similar lines, the @scheme[list-for] form iterates through a list +Along similar lines, the @scheme[for/list] form iterates through a list and implicitly accumulates each result into a list: @interaction[ -(list-for ([i (list "peanuts" "popcorn" "crackerjack")]) +(for/list ([i (list "peanuts" "popcorn" "crackerjack")]) (string-append i "!")) ] -The @scheme[list-for] form is a @defterm{list compherension} form, as +The @scheme[for/list] form is a @defterm{list compherension} form, as in Haskell, Ruby, Python, and other languages. One advantage over @scheme[map] is that it can iterate over more things than just lists. -For example, @scheme[list-for] can iterate over a range of numbers: +For example, @scheme[for/list] can iterate over a range of numbers: @interaction[ -(list-for ([i (in-range 0 10)]) +(for/list ([i (in-range 0 10)]) i) ] -The @scheme[list-for] form can even iterate over a list and a range of +The @scheme[for/list] form can even iterate over a list and a range of numbers in parallel: @interaction[ -(list-for ([s (list "a" "b" "c")] +(for/list ([s (list "a" "b" "c")] [n (in-range 0 3)]) (if (= n 2) "oops!" s)) ] -Note that the binding syntax of @scheme[fold-for] and -@scheme[list-for] is similar to that of @scheme[let] (as introduced in +Note that the binding syntax of @scheme[for/fold] and +@scheme[for/list] is similar to that of @scheme[let] (as introduced in @secref["local-binding-intro"]). In the same way that @scheme[let*] -supports nested bindings, @scheme[list-for*] supports nested +supports nested bindings, @scheme[for*/list] supports nested iterations: @interaction[ -(list-for* ([s (list "a" "b" "c")] +(for*/list ([s (list "a" "b" "c")] [n (list "x" "y" "z")]) (string-append s n)) ] -Unlike the @scheme[list-for], the nested iteration of -@scheme[list-for*] covers patterns with lists not as easily expressed +Unlike the @scheme[for/list], the nested iteration of +@scheme[for*/list] covers patterns with lists not as easily expressed with @scheme[map]. When procedures like @scheme[map] suffice, however, Scheme programmers tend to use them, partly because the syntax is simpler (just a procedure call). @@ -194,7 +194,7 @@ see @secref["iterations+comprehensions"]. @;------------------------------------------------------------------------ @section{List Iteration from Scratch} -Although @scheme[map] and @scheme[list-for] are predefined, they are +Although @scheme[map] and @scheme[for/list] are predefined, they are not primitive in any interesting sense. You can write equivalent iterations using a handful of list primitives. @@ -340,11 +340,11 @@ It turns out that if you write @schemeblock[ (define (my-map f lst) - (list-for ([i lst]) + (for/list ([i lst]) (f i))) ] -then the @scheme[list-for] form in the procedure both is expanded to +then the @scheme[for/list] form in the procedure both is expanded to essentially the same code as the @scheme[iter] local definition and use. The difference is merely syntactic convenience. @@ -446,7 +446,7 @@ context. That is, a named @scheme[let] binds a procedure identifier that is visible only in the procedure's body, and it implicitly calls the procedure with the values of some initial expressions. A named -@scheme[let] looks similar to the start of @scheme[fold-for], but the +@scheme[let] looks similar to the start of @scheme[for/fold], but the recursive calls in the body are explicit, and they are not constrained to tail position. diff --git a/collects/scribblings/guide/module-basics.scrbl b/collects/scribblings/guide/module-basics.scrbl new file mode 100644 index 0000000000..91f623db6a --- /dev/null +++ b/collects/scribblings/guide/module-basics.scrbl @@ -0,0 +1,6 @@ +#reader(lib "docreader.ss" "scribble") +@require[(lib "manual.ss" "scribble")] +@require[(lib "eval.ss" "scribble")] +@require["guide-utils.ss"] + +@title{Module Basics} diff --git a/collects/scribblings/guide/paths.scrbl b/collects/scribblings/guide/paths.scrbl new file mode 100644 index 0000000000..314a687e95 --- /dev/null +++ b/collects/scribblings/guide/paths.scrbl @@ -0,0 +1,57 @@ +#reader(lib "docreader.ss" "scribble") +@require[(lib "manual.ss" "scribble")] +@require[(lib "eval.ss" "scribble")] +@require["guide-utils.ss"] + +@title[#:tag "paths"]{Paths} + +A @defterm{path} encapsulates a filesystem path that (potentially) +names a file or directory. Although paths can be converted to and from +strings and byte strings, neither strings nor byte strings are +suitable for representing general paths. The problem is that paths are +represented in the filesystem as either byte sequences or UTF-16 +sequences (depending on the operating systems); the sequences are not +always human-readable, and not all sequences can be decoded to Unicode +scalar values. + +Despite the occasional encoding problems, most paths can be converted +to and fom strings. Thus, procedures that accept a path argument +always accept a string, and the printed form of a path uses the string +decodin of the path inside @schemefont{#}. The +@scheme[display] form of a path is the same as the @scheme[display] +form of its string encodings. + +@examples[ +(string->path "my-data.txt") +(file-exists? "my-data.txt") +(file-exists? (string->path "my-data.txt")) +(display (string->path "my-data.txt")) +] + +Produces that produce references to the filesystem always produce path +values, instead of strings. + +@examples[ +(path-replace-suffix "foo.scm" #".ss") +] + +Although it's sometimes tempting to directly manipulate strings that +represent filesystem paths, correctly manipulating a path can be +surprisingly difficult. For example, if you start under Unix with the +aboslute path @file{/tmp/~} and take just the last part, you end up +with @file{~}---which looks like a reference to the current user's +home directory, instead of a relative path to a file of directory +named @file{~}. Windows path manipulation, furthermore, is far +trickier, because path elements like @file{aux} can have special +meanings (see @secref["windows-path"]). + +Use procedures like @scheme[split-path] and @scheme[build-path] to +deconstruct and construct paths. When you must manipulate the name of +a specific path element (i.e., a file or directory component in a +path), use procedures like @scheme[path-element->bytes] and +@scheme[bytes->path-element]. + +@examples[ +(build-path "easy" "file.ss") +(split-path (build-path "easy" "file.ss")) +]