revised 'for' and docs

svn: r6400
This commit is contained in:
Matthew Flatt 2007-05-30 03:10:57 +00:00
parent f70d12b1b7
commit c59c7ebab7
12 changed files with 768 additions and 188 deletions

View File

@ -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))]]))))

View File

@ -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)))))]))

View File

@ -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)

View File

@ -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)))

View File

@ -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"]

View File

@ -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].

View File

@ -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"]

View File

@ -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}

View File

@ -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]}))
]

View File

@ -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.

View File

@ -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}

View File

@ -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{#<path:} and @scheme{>}. 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"))
]