diff --git a/collects/r5rs/r5rs.scrbl b/collects/r5rs/r5rs.scrbl index 80a5356b3f..375f64d2d9 100644 --- a/collects/r5rs/r5rs.scrbl +++ b/collects/r5rs/r5rs.scrbl @@ -20,9 +20,9 @@ @title{@bold{R5RS}: Legacy Standard Language} -The ``The Revised@superscript{5} Report on the Algorithmic Language -Scheme'' @cite["Kelsey98"] defines a dialect of Scheme. We use -@defterm{@|r5rs|} to refer to both the standard and the language +The @link["../r5rs-std/index.html"]{The Revised@superscript{5} Report +on the Algorithmic Language Scheme} defines a dialect of Scheme. We +use @defterm{@|r5rs|} to refer to both the standard and the language defined by the standard. The default dialect of Scheme provided by @exec{mzscheme} and other @@ -31,6 +31,8 @@ includes tools and libraries for running @|r5rs| programs. @table-of-contents[] +@redirects[] + @; ---------------------------------------- @section[#:tag "running"]{Running @|r5rs| Programs} @@ -150,24 +152,19 @@ The @scheme[cons] of @schememodname[r5rs] corresponds to is @scheme[mcdr], and @scheme[map] is @schememodname[scheme/mpair]'s @scheme[mmap], and so on. -@defproc[(eval [expr any/c] [environment namespace?]) any]{ - An @|r5rs| @defterm{environment} is implemented as a @scheme[scheme/base] @defterm{namespace}. Also, relative to @scheme[scheme/base], the @scheme[expr] passed to @scheme[eval] is -constructed using mutable pairs.} +constructed using mutable pairs. -@defproc[(scheme-report-environment [n (one-of/c 5)]) namespace?]{ +The @scheme[scheme-report-environment] function returns a namespace +containing the bindings of @schememodname[r5rs]. Procedure values are +installed into the namespace using @scheme[namespace-require/copy], so +that they can be redefined. -Returns a namespace containing the bindings of @schememodname[r5rs]. -Procedure values are installed into the namespace using -@scheme[namespace-require/copy], so that they can be redefined.} - -@defproc[(scheme-null-environment [n (one-of/c 5)]) namespace?]{ - -Returns a namespace containing the syntactic forms of -@schememodname[r5rs], not including @scheme[#%module-begin] (which is -not useful outside of a module).} +The @scheme[scheme-null-environment] function returns a namespace +containing the syntactic forms of @schememodname[r5rs], not including +@scheme[#%module-begin] (which is not useful outside of a module). @; ---------------------------------------- @@ -194,12 +191,228 @@ importing @schememodname[r5rs/init]. @; ---------------------------------------- -@(bibliography +@(define (redirects) + (make-binding-redirect-elements + 'r5rs + (map (lambda (b) + (list (string->symbol (car b)) + (cadr b) + (build-path 'up "r5rs-std" (caddr b)) + (cadddr b))) + bindings))) - (bib-entry #:key "Kelsey98" - #:author "Richard Kelsey, William Clinger, and Jonathan Rees (editors)" - #:title @elem{The Revised@superscript{5} Report on the Algorithmic Language Scheme} - #:date "1998" - #:url "http://schemers.org/Documents/Standards/R5RS/") +@; This list was extracted from the old "keywords" and "hdindex" files: +@(define bindings + '(("lambda" #t "r5rs-Z-H-7.html" "%_idx_96") + ("set!" #t "r5rs-Z-H-7.html" "%_idx_104") + ("quote" #t "r5rs-Z-H-7.html" "%_idx_86") + ("quasiquote" #t "r5rs-Z-H-7.html" "%_idx_154") + ("or" #t "r5rs-Z-H-7.html" "%_idx_122") + ("and" #t "r5rs-Z-H-7.html" "%_idx_120") + ("define" #t "r5rs-Z-H-8.html" "%_idx_194") + ("define-syntax" #t "r5rs-Z-H-8.html" "%_idx_202") + ("let" #t "r5rs-Z-H-7.html" "%_idx_126") + ("let*" #t "r5rs-Z-H-7.html" "%_idx_130") + ("let-syntax" #t "r5rs-Z-H-7.html" "%_idx_184") + ("letrec" #t "r5rs-Z-H-7.html" "%_idx_134") + ("letrec-syntax" #t "r5rs-Z-H-7.html" "%_idx_186") + ("cond" #t "r5rs-Z-H-7.html" "%_idx_108") + ("delay" #t "r5rs-Z-H-7.html" "%_idx_146") + ("do" #t "r5rs-Z-H-7.html" "%_idx_140") + ("if" #t "r5rs-Z-H-7.html" "%_idx_98") + ("case" #t "r5rs-Z-H-7.html" "%_idx_116") + ("begin" #t "r5rs-Z-H-7.html" "%_idx_138") -) + ("zero?" #f "r5rs-Z-H-9.html" "%_idx_268") + ("write-char" #f "r5rs-Z-H-9.html" "%_idx_666") + ;; ("write-char" #f "r5rs-Z-H-9.html" "%_idx_664") + ("write" #f "r5rs-Z-H-9.html" "%_idx_654") + ;; ("write" #f "r5rs-Z-H-9.html" "%_idx_652") + ("with-output-to-file" #f "r5rs-Z-H-9.html" "%_idx_624") + ("with-input-from-file" #f "r5rs-Z-H-9.html" "%_idx_622") + ("vector?" #f "r5rs-Z-H-9.html" "%_idx_556") + ("vector-set!" #f "r5rs-Z-H-9.html" "%_idx_568") + ("vector-ref" #f "r5rs-Z-H-9.html" "%_idx_566") + ("vector-length" #f "r5rs-Z-H-9.html" "%_idx_564") + ("vector-fill!" #f "r5rs-Z-H-9.html" "%_idx_574") + ("vector->list" #f "r5rs-Z-H-9.html" "%_idx_570") + ("vector" #f "r5rs-Z-H-9.html" "%_idx_562") + ("values" #f "r5rs-Z-H-9.html" "%_idx_594") + ("truncate" #f "r5rs-Z-H-9.html" "%_idx_318") + ("transcript-on" #f "r5rs-Z-H-9.html" "%_idx_670") + ("transcript-off" #f "r5rs-Z-H-9.html" "%_idx_672") + ("tan" #f "r5rs-Z-H-9.html" "%_idx_334") + ("symbol?" #f "r5rs-Z-H-9.html" "%_idx_460") + ("symbol->string" #f "r5rs-Z-H-9.html" "%_idx_462") + ("substring" #f "r5rs-Z-H-9.html" "%_idx_542") + ("string?" #f "r5rs-Z-H-9.html" "%_idx_508") + ("string>?" #f "r5rs-Z-H-9.html" "%_idx_528") + ("string>=?" #f "r5rs-Z-H-9.html" "%_idx_532") + ("string=?" #f "r5rs-Z-H-9.html" "%_idx_522") + ("string?" #f "r5rs-Z-H-9.html" "%_idx_536") + ("string-ci>=?" #f "r5rs-Z-H-9.html" "%_idx_540") + ("string-ci=?" #f "r5rs-Z-H-9.html" "%_idx_524") + ("string-cisymbol" #f "r5rs-Z-H-9.html" "%_idx_464") + ("string->number" #f "r5rs-Z-H-9.html" "%_idx_370") + ;; ("string->number" #f "r5rs-Z-H-9.html" "%_idx_368") + ("string->list" #f "r5rs-Z-H-9.html" "%_idx_546") + ("string" #f "r5rs-Z-H-9.html" "%_idx_514") + ("sqrt" #f "r5rs-Z-H-9.html" "%_idx_344") + ("sin" #f "r5rs-Z-H-9.html" "%_idx_330") + ("set-cdr!" #f "r5rs-Z-H-9.html" "%_idx_418") + ("set-car!" #f "r5rs-Z-H-9.html" "%_idx_416") + ("scheme-report-environment" #f "r5rs-Z-H-9.html" "%_idx_602") + ("round" #f "r5rs-Z-H-9.html" "%_idx_320") + ("reverse" #f "r5rs-Z-H-9.html" "%_idx_440") + ("remainder" #f "r5rs-Z-H-9.html" "%_idx_302") + ("real?" #f "r5rs-Z-H-9.html" "%_idx_248") + ("real-part" #f "r5rs-Z-H-9.html" "%_idx_352") + ("read-char" #f "r5rs-Z-H-9.html" "%_idx_640") + ;; ("read-char" #f "r5rs-Z-H-9.html" "%_idx_638") + ("read" #f "r5rs-Z-H-9.html" "%_idx_636") + ;; ("read" #f "r5rs-Z-H-9.html" "%_idx_634") + ("rationalize" #f "r5rs-Z-H-9.html" "%_idx_322") + ("rational?" #f "r5rs-Z-H-9.html" "%_idx_250") + ("quotient" #f "r5rs-Z-H-9.html" "%_idx_300") + ("procedure?" #f "r5rs-Z-H-9.html" "%_idx_576") + ("positive?" #f "r5rs-Z-H-9.html" "%_idx_270") + ("peek-char" #f "r5rs-Z-H-9.html" "%_idx_644") + ;; ("peek-char" #f "r5rs-Z-H-9.html" "%_idx_642") + ("pair?" #f "r5rs-Z-H-9.html" "%_idx_406") + ("output-port?" #f "r5rs-Z-H-9.html" "%_idx_616") + ("open-output-file" #f "r5rs-Z-H-9.html" "%_idx_628") + ("open-input-file" #f "r5rs-Z-H-9.html" "%_idx_626") + ("odd?" #f "r5rs-Z-H-9.html" "%_idx_274") + ("numerator" #f "r5rs-Z-H-9.html" "%_idx_310") + ("number?" #f "r5rs-Z-H-9.html" "%_idx_244") + ("number->string" #f "r5rs-Z-H-9.html" "%_idx_366") + ;; ("number->string" #f "r5rs-Z-H-9.html" "%_idx_364") + ("null?" #f "r5rs-Z-H-9.html" "%_idx_428") + ("null-environment" #f "r5rs-Z-H-9.html" "%_idx_604") + ("not" #f "r5rs-Z-H-9.html" "%_idx_386") + ("newline" #f "r5rs-Z-H-9.html" "%_idx_662") + ;; ("newline" #f "r5rs-Z-H-9.html" "%_idx_660") + ("negative?" #f "r5rs-Z-H-9.html" "%_idx_272") + ("modulo" #f "r5rs-Z-H-9.html" "%_idx_304") + ("min" #f "r5rs-Z-H-9.html" "%_idx_280") + ("memv" #f "r5rs-Z-H-9.html" "%_idx_448") + ("memq" #f "r5rs-Z-H-9.html" "%_idx_446") + ("member" #f "r5rs-Z-H-9.html" "%_idx_450") + ("max" #f "r5rs-Z-H-9.html" "%_idx_278") + ("map" #f "r5rs-Z-H-9.html" "%_idx_580") + ("make-vector" #f "r5rs-Z-H-9.html" "%_idx_560") + ;; ("make-vector" #f "r5rs-Z-H-9.html" "%_idx_558") + ("make-string" #f "r5rs-Z-H-9.html" "%_idx_512") + ;; ("make-string" #f "r5rs-Z-H-9.html" "%_idx_510") + ("make-rectangular" #f "r5rs-Z-H-9.html" "%_idx_348") + ("make-polar" #f "r5rs-Z-H-9.html" "%_idx_350") + ("magnitude" #f "r5rs-Z-H-9.html" "%_idx_356") + ("log" #f "r5rs-Z-H-9.html" "%_idx_328") + ("load" #f "r5rs-Z-H-9.html" "%_idx_668") + ("list?" #f "r5rs-Z-H-9.html" "%_idx_432") + ("list-tail" #f "r5rs-Z-H-9.html" "%_idx_442") + ("list-ref" #f "r5rs-Z-H-9.html" "%_idx_444") + ("list->vector" #f "r5rs-Z-H-9.html" "%_idx_572") + ("list->string" #f "r5rs-Z-H-9.html" "%_idx_548") + ("list" #f "r5rs-Z-H-9.html" "%_idx_434") + ("length" #f "r5rs-Z-H-9.html" "%_idx_436") + ("lcm" #f "r5rs-Z-H-9.html" "%_idx_308") + ("interaction-environment" #f "r5rs-Z-H-9.html" "%_idx_606") + ("integer?" #f "r5rs-Z-H-9.html" "%_idx_252") + ("integer->char" #f "r5rs-Z-H-9.html" "%_idx_500") + ("input-port?" #f "r5rs-Z-H-9.html" "%_idx_614") + ("inexact?" #f "r5rs-Z-H-9.html" "%_idx_256") + ("inexact->exact" #f "r5rs-Z-H-9.html" "%_idx_362") + ("imag-part" #f "r5rs-Z-H-9.html" "%_idx_354") + ("gcd" #f "r5rs-Z-H-9.html" "%_idx_306") + ("force" #f "r5rs-Z-H-9.html" "%_idx_584") + ("for-each" #f "r5rs-Z-H-9.html" "%_idx_582") + ("floor" #f "r5rs-Z-H-9.html" "%_idx_314") + ("expt" #f "r5rs-Z-H-9.html" "%_idx_346") + ("exp" #f "r5rs-Z-H-9.html" "%_idx_326") + ("exact?" #f "r5rs-Z-H-9.html" "%_idx_254") + ("exact->inexact" #f "r5rs-Z-H-9.html" "%_idx_360") + ("even?" #f "r5rs-Z-H-9.html" "%_idx_276") + ("eval" #f "r5rs-Z-H-9.html" "%_idx_600") + ("eqv?" #f "r5rs-Z-H-9.html" "%_idx_214") + ("equal?" #f "r5rs-Z-H-9.html" "%_idx_222") + ("eq?" #f "r5rs-Z-H-9.html" "%_idx_220") + ("eof-object?" #f "r5rs-Z-H-9.html" "%_idx_646") + ("dynamic-wind" #f "r5rs-Z-H-9.html" "%_idx_598") + ("display" #f "r5rs-Z-H-9.html" "%_idx_658") + ;; ("display" #f "r5rs-Z-H-9.html" "%_idx_656") + ("denominator" #f "r5rs-Z-H-9.html" "%_idx_312") + ("current-output-port" #f "r5rs-Z-H-9.html" "%_idx_620") + ("current-input-port" #f "r5rs-Z-H-9.html" "%_idx_618") + ("cos" #f "r5rs-Z-H-9.html" "%_idx_332") + ("cons" #f "r5rs-Z-H-9.html" "%_idx_408") + ("complex?" #f "r5rs-Z-H-9.html" "%_idx_246") + ("close-output-port" #f "r5rs-Z-H-9.html" "%_idx_632") + ("close-input-port" #f "r5rs-Z-H-9.html" "%_idx_630") + ("char?" #f "r5rs-Z-H-9.html" "%_idx_466") + ("char>?" #f "r5rs-Z-H-9.html" "%_idx_472") + ("char>=?" #f "r5rs-Z-H-9.html" "%_idx_476") + ("char=?" #f "r5rs-Z-H-9.html" "%_idx_468") + ("char?" #f "r5rs-Z-H-9.html" "%_idx_482") + ("char-ci>=?" #f "r5rs-Z-H-9.html" "%_idx_486") + ("char-ci=?" #f "r5rs-Z-H-9.html" "%_idx_478") + ("char-ciinteger" #f "r5rs-Z-H-9.html" "%_idx_498") + ("ceiling" #f "r5rs-Z-H-9.html" "%_idx_316") + ("cdr" #f "r5rs-Z-H-9.html" "%_idx_414") + ("cddddr" #f "r5rs-Z-H-9.html" "%_idx_426") + ("cdddar" #f "r5rs-Z-H-9.html" "%_idx_424") + ("car" #f "r5rs-Z-H-9.html" "%_idx_410") + ("call-with-values" #f "r5rs-Z-H-9.html" "%_idx_596") + ("call-with-output-file" #f "r5rs-Z-H-9.html" "%_idx_612") + ("call-with-input-file" #f "r5rs-Z-H-9.html" "%_idx_610") + ("call-with-current-continuation" #f "r5rs-Z-H-9.html" "%_idx_588") + ("cadr" #f "r5rs-Z-H-9.html" "%_idx_422") + ("caar" #f "r5rs-Z-H-9.html" "%_idx_420") + ("boolean?" #f "r5rs-Z-H-9.html" "%_idx_388") + ("atan" #f "r5rs-Z-H-9.html" "%_idx_342") + ;; ("atan" #f "r5rs-Z-H-9.html" "%_idx_340") + ("assv" #f "r5rs-Z-H-9.html" "%_idx_454") + ("assq" #f "r5rs-Z-H-9.html" "%_idx_452") + ("assoc" #f "r5rs-Z-H-9.html" "%_idx_456") + ("asin" #f "r5rs-Z-H-9.html" "%_idx_336") + ("apply" #f "r5rs-Z-H-9.html" "%_idx_578") + ("append" #f "r5rs-Z-H-9.html" "%_idx_438") + ("angle" #f "r5rs-Z-H-9.html" "%_idx_358") + ("acos" #f "r5rs-Z-H-9.html" "%_idx_338") + ("abs" #f "r5rs-Z-H-9.html" "%_idx_298") + (">=" #f "r5rs-Z-H-9.html" "%_idx_266") + (">" #f "r5rs-Z-H-9.html" "%_idx_262") + ("=" #f "r5rs-Z-H-9.html" "%_idx_258") + ("<=" #f "r5rs-Z-H-9.html" "%_idx_264") + ("<" #f "r5rs-Z-H-9.html" "%_idx_260") + ("/" #f "r5rs-Z-H-9.html" "%_idx_296") + ;;("/" #f "r5rs-Z-H-9.html" "%_idx_294") + ;;("/" #f "r5rs-Z-H-9.html" "%_idx_292") + ("-" #f "r5rs-Z-H-9.html" "%_idx_290") + ;;("-" #f "r5rs-Z-H-9.html" "%_idx_288") + ;;("-" #f "r5rs-Z-H-9.html" "%_idx_286") + ("+" #f "r5rs-Z-H-9.html" "%_idx_282") + ("*" #f "r5rs-Z-H-9.html" "%_idx_284"))) diff --git a/collects/scheme/help.ss b/collects/scheme/help.ss index 585a4c4aa9..e15da26271 100644 --- a/collects/scheme/help.ss +++ b/collects/scheme/help.ss @@ -80,7 +80,7 @@ (error 'help "no documentation found for: ~e provided by: ~a" (syntax-e id) - (module-path-index-resolve (caddr b))))) + (module-path-index-resolve (caddr (or lb b)))))) (search-for-exports xref (syntax-e id))))) (define (search-for-exports xref sym) diff --git a/collects/scribble/base-render.ss b/collects/scribble/base-render.ss index 7daf2acf1a..3ade2a645c 100644 --- a/collects/scribble/base-render.ss +++ b/collects/scribble/base-render.ss @@ -111,10 +111,16 @@ (unless (null? parts) (let ([s (car parts)]) (collect-part s d p-ci - (cons (if (unnumbered-part? s) #f pos) + (cons (if (or (unnumbered-part? s) + (part-style? s 'unnumbered)) + #f + pos) number)) (loop (cdr parts) - (if (unnumbered-part? s) pos (add1 pos)))))) + (if (or (unnumbered-part? s) + (part-style? s 'unnumbered)) + pos + (add1 pos)))))) (let ([prefix (part-tag-prefix d)]) (for ([(k v) (collect-info-ht p-ci)]) (when (cadr k) diff --git a/collects/scribble/html-render.ss b/collects/scribble/html-render.ss index ddb422c252..85d981ea74 100644 --- a/collects/scribble/html-render.ss +++ b/collects/scribble/html-render.ss @@ -9,6 +9,7 @@ setup/main-collects mzlib/list net/url + scheme/serialize (prefix-in xml: xml/xml) (for-syntax scheme/base)) (provide render-mixin @@ -49,23 +50,27 @@ ;; (i.e., the ones that are not allowed as-in in URI ;; codecs) by using "~" followed by a hex encoding. (define (anchor-name v) - (let loop ([s (format "~a" v)]) - (cond - [(regexp-match-positions #rx"[A-Z.]" s) - => (lambda (m) - (string-append - (loop (substring s 0 (caar m))) - "." - (substring s (caar m) (cdar m)) - (loop (substring s (cdar m)))))] - [(regexp-match-positions #rx"[^-a-zA-Z0-9_!*'().]" s) - => (lambda (m) - (string-append - (substring s 0 (caar m)) - "~" - (format "~x" (char->integer (string-ref s (caar m)))) - (loop (substring s (cdar m)))))] - [else s]))) + (if (literal-anchor? v) + (literal-anchor-string v) + (let loop ([s (format "~a" v)]) + (cond + [(regexp-match-positions #rx"[A-Z.]" s) + => (lambda (m) + (string-append + (loop (substring s 0 (caar m))) + "." + (substring s (caar m) (cdar m)) + (loop (substring s (cdar m)))))] + [(regexp-match-positions #rx"[^-a-zA-Z0-9_!*'().]" s) + => (lambda (m) + (string-append + (substring s 0 (caar m)) + "~" + (format "~x" (char->integer (string-ref s (caar m)))) + (loop (substring s (cdar m)))))] + [else s])))) + + (define-serializable-struct literal-anchor (string)) (define literal (let ([loc (xml:make-location 0 0 0)]) @@ -229,10 +234,18 @@ (let ([key (generate-tag (target-element-tag i) ci)]) (collect-put! ci key - (vector (path->relative (current-output-file)) - #f + (vector (path->relative (let ([p (current-output-file)]) + (if (redirect-target-element? i) + (let-values ([(base name dir?) (split-path p)]) + (build-path + base + (redirect-target-element-alt-path i))) + p))) + #f (page-target-element? i) - key)))) + (if (redirect-target-element? i) + (make-literal-anchor (redirect-target-element-alt-anchor i)) + key))))) (define (dest-path dest) (if (vector? dest) ; temporary diff --git a/collects/scribble/latex-render.ss b/collects/scribble/latex-render.ss index 8f0a31a087..3eaf7b86fd 100644 --- a/collects/scribble/latex-render.ss +++ b/collects/scribble/latex-render.ss @@ -68,7 +68,8 @@ (if no-number? "*" "")) - (when (not (part-style? d 'hidden)) + (when (not (or (part-style? d 'hidden) + no-number?)) (printf "[") (parameterize ([disable-images #t]) (render-content (part-title-content d) d ri)) diff --git a/collects/scribble/manual.ss b/collects/scribble/manual.ss index d7fcf2ea10..6873dec8f5 100644 --- a/collects/scribble/manual.ss +++ b/collects/scribble/manual.ss @@ -437,6 +437,55 @@ (lambda () (car content)) (lambda () (car content)))))) + + (define (make-binding-redirect-elements mod-path redirects) + (let ([taglet (path->main-collects-relative + (resolved-module-path-name + (module-path-index-resolve + (module-path-index-join mod-path #f))))]) + (make-element + #f + (map + (lambda (redirect) + (let ([id (car redirect)] + [form? (cadr redirect)] + [path (caddr redirect)] + [anchor (cadddr redirect)]) + (let ([make-one + (lambda (kind) + (make-redirect-target-element + #f + null + (list kind (list taglet id)) + path + anchor))]) + (make-element + #f + (list (make-one (if form? 'form 'def)) + (make-one 'dep) + (make-index-element #f + null + (list (if form? 'form 'def) + (list taglet id)) + (list (symbol->string id)) + (list + (make-element + "schemesymbol" + (list + (make-element + (if form? + "schemesyntaxlink" + "schemevaluelink") + (list (symbol->string id)))))) + ((if form? + make-form-index-desc + make-procedure-index-desc) + id + (list mod-path)))))))) + redirects)))) + + (provide make-binding-redirect-elements) + (define current-signature (make-parameter #f)) (define-syntax-rule (sigelem sig elem) diff --git a/collects/scribble/struct.ss b/collects/scribble/struct.ss index f2ebedb900..a507653c1f 100644 --- a/collects/scribble/struct.ss +++ b/collects/scribble/struct.ss @@ -159,6 +159,8 @@ [(target-element element) ([tag tag?])] [(toc-target-element target-element) ()] [(page-target-element target-element) ()] + [(redirect-target-element target-element) ([alt-path path-string?] + [alt-anchor string?])] [(link-element element) ([tag tag?])] [(index-element element) ([tag tag?] [plain-seq (listof string?)] diff --git a/collects/srfi/info.ss b/collects/srfi/info.ss new file mode 100644 index 0000000000..ef209766f2 --- /dev/null +++ b/collects/srfi/info.ss @@ -0,0 +1,3 @@ +#lang setup/infotab + +(define scribblings '(("srfi.scrbl" (multi-page)))) diff --git a/collects/srfi/srfi.scrbl b/collects/srfi/srfi.scrbl new file mode 100644 index 0000000000..e9334b1fd5 --- /dev/null +++ b/collects/srfi/srfi.scrbl @@ -0,0 +1,898 @@ +#lang scribble/doc +@(require scribble/manual + (for-syntax scheme/base)) + +@(define-syntax (srfi stx) + (syntax-case stx () + [(_ num #:subdir dir . title) + (with-syntax ([srfi/n (string->symbol + (format "srfi/~a" (syntax-e #'num)))]) + #'(begin + (section #:tag (format "srfi-~a" num) + #:style 'unnumbered + (format "SRFI ~a: " num) + . title) + (defmodule srfi/n) + "Original specification: " @link[(format "../srfi-std/~asrfi-~a.html" dir num) + "SRFI " + (number->string num)]))] + [(_ num . title) #'(srfi num #:subdir "" . title)])) + +@;{ The `lst' argument is a list of + (list sym syntactic-form? html-anchor) } +@(define (redirect n lst) + (let ([file (format "srfi-~a.html" n)] + [mod-path (string->symbol (format "srfi/~a" n))]) + (make-binding-redirect-elements mod-path + (map (lambda (b) + (list (car b) (cadr b) + (build-path 'up "srfi-std" file) + (caddr b))) + lst)))) + +@(define in-core + (case-lambda + [() (in-core ".")] + [(k) + @elem{This SRFI's bindings are also available in @schememodname[scheme/base]@|k|}])) + +@; ---------------------------------------------------------------------- + +@title{@bold{SRFIs}: Libraries} + +The @link["http://srfi.schemers.org/"]{Scheme Requests for +Implementation} (a.k.a. @deftech{SRFI}) process allows the Scheme +community as a whole to propose extensions of Scheme to be supported +by multiple implementations. + +PLT Scheme is distributed with implementations of many SRFIs, most of +which can be implemented as libraries. To import the bindings of SRFI +@math{n}, use + +@schemeblock[ +(require #, @elem{@schemeidfont{srfi/}@math{n}}) +] + +This document lists the SRFIs that are supported by PLT Scheme and +provides a link to the original SRFI specification (which is also +distributed as part of PLT Scheme's documentation). + +@table-of-contents[] + +@; ---------------------------------------- + +@srfi[1]{List Library} + +@redirect[1 '( + (cons #f "cons") + (list #f "list") + (xcons #f "xcons") + (cons* #f "cons*") + (make-list #f "make-list") + (list-tabulate #f "list-tabulate") + (list-copy #f "list-copy") + (circular-list #f "circular-list") + (iota #f "iota") + (pair? #f "pair-p") + (null? #f "null-p") + (proper-list? #f "proper-list-p") + (circular-list? #f "circular-list-p") + (dotted-list? #f "dotted-list-p") + (not-pair? #f "not-pair-p") + (null-list? #f "null-list-p") + (car #f "car") + (cdr #f "cdr") + (cddadr #f "cddadr") + (cddddr #f "cddddr") + (list-ref #f "list-ref") + (first #f "first") + (second #f "second") + (third #f "third") + (fourth #f "fourth") + (fifth #f "fifth") + (sixth #f "sixth") + (seventh #f "seventh") + (eighth #f "eighth") + (ninth #f "ninth") + (tenth #f "tenth") + (car+cdr #f "car+cdr") + (take #f "take") + (drop #f "drop") + (take-right #f "take-right") + (drop-right #f "drop-right") + (take! #f "take!") + (drop-right! #f "drop-right!") + (split-at #f "split-at") + (split-at! #f "split-at!") + (last #f "last") + (last-pair #f "last-pair") + (length #f "length") + (length+ #f "length+") + (append #f "append") + (append! #f "append!") + (concatenate #f "concatenate") + (concatenate! #f "concatenate!") + (reverse #f "reverse") + (reverse! #f "srfi-1.html") + (append-reverse #f "append-reverse") + (append-reverse! #f "append-reverse!") + (zip #f "zip") + (unzip1 #f "unzip1") + (unzip2 #f "unzip2") + (unzip3 #f "unzip3") + (unzip4 #f "unzip4") + (unzip5 #f "unzip5") + (count #f "count") + (fold #f "fold") + (fold-right #f "fold-right") + (pair-fold #f "pair-fold") + (pair-fold-right #f "pair-fold-right") + (reduce #f "reduce") + (reduce-right #f "reduce-right") + (unfold #f "unfold") + (map #f "map") + (for-each #f "srfi-1.html") + (append-map #f "append-map") + (append-map! #f "append-map!") + (map! #f "map!") + (map-in-order #f "map-in-order") + (pair-for-each #f "pair-for-each") + (filter-map #f "filter-map") + (filter #f "filter") + (filter! #f "filter!") + (partition! #f "partition!") + (remove! #f "remove!") + (remove #f "remove") + (partition #f "partition") + (find #f "find") + (find-tail #f "find-tail") + (take-while #f "take-while") + (take-while! #f "take-while!") + (drop-while #f "drop-while") + (span #f "span") + (span! #f "span!") + (break #f "break") + (break! #f "break!") + (any #f "any") + (every #f "every") + (list-index #f "list-index") + (member #f "member") + (memq #f "memq") + (memv #f "memv") + (delete #f "delete") + (delete-duplicates #f "delete-duplicates") + (delete! #f "delete!") + (delete-duplicates! #f "delete-duplicates!") + (assoc #f "assoc") + (assq #f "assq") + (assv #f "assv") + (alist-cons #f "alist-cons") + (alist-copy #f "alist-copy") + (alist-delete #f "alist-delete") + (alist-delete! #f "alist-delete!") + (lset #f "lset") + (lset= #f "lset=") + (lset-adjoin #f "lset-adjoin") + (lset-union #f "lset-union") + (lset-intersection #f "lset-intersection") + (lset-intersection! #f "lset-intersection!") + (lset-union! #f "lset-union!") + (lset-difference! #f "lset-difference!") + (lset-xor! #f "lset-xor!") + (lset-diff+intersection! #f "lset-diff+intersection!") + (lset-difference #f "lset-difference") + (lset-xor #f "lset-xor") + (lset-diff+intersection #f "lset-diff+intersection") + (set-car! #f "set-car!") + (set-cdr! #f "set-cdr!") +)] + +@; ---------------------------------------- + +@srfi[2]{AND-LET*: an AND with local bindings...} + +@redirect[2 '( + (and-let* #t "and-let") +)] + +@; ---------------------------------------- + +@srfi[4]{Homogeneous numeric vector datatypes} + +@redirect[4 '( + (s8vector #f "s8vector") + (u8vector #f "s8vector") + (s16vector #f "s8vector") + (u16vector #f "u8vector") + (s32vector #f "s16vector") + (u32vector #f "u16vector") + (s64vector #f "s64vector") + (u64vector #f "u64vector") + (f32vector #f "f32vector") + (f64vector #f "f64vector") +)] + +This SRFI's reader and printer syntax is not supported. The bindings +are also available from @schememodname[scheme/foreign]. + +@; ---------------------------------------- + +@srfi[5]{A compatible let form with signatures and rest arguments} + +@redirect[5 '( + (let #t "unnamed") +)] + +@; ---------------------------------------- + +@srfi[6]{Basic String Ports} + +@redirect[6 '( + (open-input-string #f "open-input-string") + (open-output-string #f "open-output-string") + (get-output-string #f "get-output-string") +)] + +@in-core[] + +@; ---------------------------------------- + +@srfi[7]{Feature-based program configuration language} + +@redirect[7 '( + (program #t "program") +)] + +@; ---------------------------------------- + +@srfi[8]{RECEIVE: Binding to multiple values} + +@redirect[8 '( + (receive #t "receive") +)] + +@; ---------------------------------------- + +@srfi[9]{Defining Record Types} + +@redirect[9 '( + (define-record-type #t "define-record-type") +)] + +@; ---------------------------------------- + +@srfi[11]{Syntax for receiving multiple values} + +@redirect[11 '( + (let-values #t "let-values") + (let*-values #t "let*-values") +)] + +@in-core{, but without support for dotted ``rest'' bindings.} + +@; ---------------------------------------- + +@srfi[13]{String Libraries} + +@redirect[13 '( + (string? #f "string-p") + (string-null? #f "string-null-p") + (string-every #f "string-every") + (string-any #f "string-any") + (make-string #f "make-string") + (string #f "string") + (string-tabulate #f "string-tabulate") + (string->list #f "string2list") + (list->string #f "list2string") + (reverse-list->string #f "reverse-list2string") + (string-join #f "string-join") + (string-length #f "string-length") + (string-ref #f "string-ref") + (string-copy #f "string-copy") + (substring/shared #f "substring/shared") + (string-copy! #f "string-copy!") + (string-take #f "string-take") + (string-drop #f "string-drop") + (string-take-right #f "string-take-right") + (string-drop-right #f "string-drop-right") + (string-pad #f "string-pad") + (string-pad-right #f "string-pad-right") + (string-trim #f "string-trim") + (string-trim-right #f "string-trim-right") + (string-trim-both #f "string-trim-both") + (string-set! #f "string-set!") + (string-fill! #f "string-fill!") + (string-compare #f "string-compare") + (string-compare-ci #f "string-compare-ci") + (string= #f "string=") + (string<> #f "string<>") + (string< #f "string<") + (string> #f "string>") + (string<= #f "string<=") + (string>= #f "string>=") + (string-ci= #f "string-ci=") + (string-ci<> #f "string-ci<>") + (string-ci< #f "string-ci<") + (string-ci> #f "string-ci>") + (string-ci<= #f "string-ci<=") + (string-ci>= #f "string-ci>=") + (string-hash #f "string-hash") + (string-hash-ci #f "string-hash-ci") + (string-prefix-length #f "string-prefix-length") + (string-suffix-length #f "string-suffix-length") + (string-prefix-length-ci #f "string-prefix-length-ci") + (string-suffix-length-ci #f "string-suffix-length-ci") + (string-prefix? #f "string-prefix-p") + (string-suffix? #f "string-suffix-p") + (string-prefix-ci? #f "string-prefix-ci-p") + (string-suffix-ci? #f "string-suffix-ci-p") + (string-index #f "string-index") + (string-index-right #f "string-index-right") + (string-skip #f "string-skip") + (string-skip-right #f "string-skip-right") + (string-count #f "string-count") + (string-contains #f "string-contains") + (string-contains-ci #f "string-contains-ci") + (string-titlecase #f "string-titlecase") + (string-titlecase! #f "string-titlecase!") + (string-upcase #f "string-upcase") + (string-upcase! #f "string-upcase!") + (string-downcase #f "string-downcase") + (string-downcase! #f "string-downcase!") + (string-reverse #f "string-reverse") + (string-reverse! #f "string-reverse!") + (string-append #f "string-append") + (string-concatenate #f "string-concatenate") + (string-concatenate/shared #f "string-concatenate/shared") + (string-append/shared #f "string-append/shared") + (string-concatenate-reverse #f "string-concatenate-reverse") + (string-concatenate-reverse/shared #f "string-concatenate-reverse/shared") + (string-map #f "string-map") + (string-map! #f "string-map!") + (string-fold #f "string-fold") + (string-fold-right #f "string-fold-right") + (string-unfold #f "string-unfold") + (string-unfold-right #f "string-unfold-right") + (string-for-each #f "string-for-each") + (string-for-each-index #f "string-for-each-index") + (xsubstring #f "xsubstring") + (string-xcopy! #f "string-xcopy!") + (string-replace #f "string-replace") + (string-tokenize #f "string-tokenize") + (string-filter #f "string-filter") + (string-delete #f "string-delete") + (string-parse-start+end #f "string-parse-start+end") + (string-parse-final-start+end #f "string-parse-final-start+end") + (let-string-start+end #f "let-string-start+end") + (check-substring-spec #f "check-substring-spec") + (substring-spec-ok? #f "substring-spec-ok-p") + (make-kmp-restart-vector #f "make-kmp-restart-vector") + (kmp-step #f "kmp-step") + (string-kmp-partial-search #f "string-kmp-partial-search") +)] + +@; ---------------------------------------- + +@srfi[14]{Character-set Library} + +@redirect[14 '( + (char-set? #f "char-set-p") + (char-set= #f "char-set=") + (char-set<= #f "char-set<=") + (char-set-hash #f "char-set-hash") + (char-set-cursor #f "char-set-cursor") + (char-set-ref #f "char-set-ref") + (char-set-cursor-next #f "char-set-cursor-next") + (end-of-char-set? #f "end-of-char-set-p") + (char-set-fold #f "char-set-fold") + (char-set-unfold #f "char-set-unfold") + (char-set-unfold! #f "char-set-unfold!") + (char-set-for-each #f "char-set-for-each") + (char-set-map #f "char-set-map") + (char-set-copy #f "char-set-copy") + (char-set #f "char-set") + (list->char-set #f "list->char-set") + (list->char-set! #f "list->char-set!") + (string->char-set #f "string->char-set") + (string->char-set! #f "string->char-set!") + (char-set-filter #f "char-set-filter") + (char-set-filter! #f "char-set-filter!") + (ucs-range->char-set #f "ucs-range->char-set") + (ucs-range->char-set! #f "ucs-range->char-set!") + (->char-set #f "->char-set") + (char-set->list #f "char-set->list") + (char-set->string #f "char-set->string") + (char-set-size #f "char-set-size") + (char-set-count #f "char-set-count") + (char-set-every #f "char-set-every") + (char-set-any #f "char-set-any") + (char-set-contains? #f "char-set-contains-p") + (char-set-adjoin #f "char-set-adjoin") + (char-set-delete #f "char-set-delete") + (char-set-adjoin! #f "char-set-adjoin!") + (char-set-delete! #f "char-set-delete!") + (char-set-complement #f "char-set-complement") + (char-set-union #f "char-set-union") + (char-set-intersection #f "char-set-intersection") + (char-set-difference #f "char-set-difference") + (char-set-xor #f "char-set-xor") + (char-set-diff+intersection #f "char-set-diff+intersection") + (char-set-complement! #f "char-set-complement!") + (char-set-union! #f "char-set-union!") + (char-set-intersection! #f "char-set-intersection!") + (char-set-difference! #f "char-set-difference!") + (char-set-xor! #f "char-set-xor!") + (char-set-diff+intersection! #f "char-set-diff+intersection!") + (char-set:lower-case #f "char-set:lower-case") + (char-set:upper-case #f "char-set:upper-case") + (char-set:title-case #f "char-set:title-case") + (char-set:letter #f "char-set:letter") + (char-set:digit #f "char-set:digit") + (char-set:letter+digit #f "char-set:letter+digit") + (char-set:graphic #f "char-set:graphic") + (char-set:printing #f "char-set:printing") + (char-set:whitespace #f "char-set:whitespace") + (char-set:iso-control #f "char-set:iso-control") + (char-set:punctuation #f "char-set:punctuation") + (char-set:symbol #f "char-set:symbol") + (char-set:hex-digit #f "char-set:hex-digit") + (char-set:blank #f "char-set:blank") + (char-set:ascii #f "char-set:ascii") + (char-set:empty #f "char-set:empty") + (char-set:full #f "char-set:full") +)] + +@; ---------------------------------------- + +@srfi[16]{Syntax for procedures of variable arity} + +@redirect[16 '( + (case-lambda #t "case-lambda") +)] + +@in-core[] + +@; ---------------------------------------- + +@srfi[17]{Generalized set!} + +@redirect[17 '( + (set! #t "set!") + (getter-with-setter #f "getter-with-setter") +)] + +@; ---------------------------------------- + +@srfi[19]{Time Data Types and Procedures} + +@redirect[19 '( + (time-duration #f "") + (time-monotonic #f "time-monotonic") + (time-process #f "time-process") + (time-tai #f "time-tai") + (time-thread #f "time-thread") + (time-utc #f "time-utc") + (current-date #f "") + (current-julian-day #f "current-julian-day") + (current-modified-julian-day #f "current-modified-julian-day") + (current-time #f "current-time") + (time-resolution #f "time-resolution") + (make-time #f "make-time") + (time? #f "time-p") + (time-type #f "time-type") + (time-nanosecond #f "time-nanosecond") + (time-second #f "time-second") + (set-time-type! #f "set-time-type!") + (set-time-nanosecond! #f "set-time-nanosecond!") + (set-time-second! #f "set-time-second!") + (copy-time #f "copy-time") + (time<=? #f "time<=-p") + (time=? #f "time>=-p") + (time>? #f "time>-p") + (time-difference #f "time-difference") + (time-difference! #f "time-difference!") + (add-duration #f "add-duration") + (add-duration! #f "add-duration!") + (subtract-duration #f "subtract-duration") + (subtract-duration! #f "subtract-duration!") + (make-date #f "make-date") + (date? #f "date-p") + (date-nanosecond #f "date-nanosecond") + (date-second #f "date-second") + (date-minute #f "date-minute") + (date-hour #f "date-hour") + (date-day #f "date-day") + (date-month #f "date-month") + (date-year #f "date-year") + (date-zone-offset #f "date-zone-offset") + (date-year-day #f "date-year-day") + (date-week-day #f "date-week-day") + (date-week-number #f "date-week-number") + (date->julian-day #f "date->julian-day") + (date->modified-julian-day #f "date->modified-julian-day") + (date->time-monotonic #f "date->time-monotonic") + (date->time-tai #f "date->time-tai") + (date->time-utc #f "date->time-utc") + (julian-day->date #f "julian-day->date") + (julian-day->time-monotonic #f "julian-day->time-monotonic") + (julian-day->time-tai #f "julian-day->time-tai") + (julian-day->time-utc #f "julian-day->time-utc") + (modified-julian-day->date #f "modified-julian-day->date") + (modified-julian-day->time-monotonic #f "modified-julian-day->time-monotonic") + (modified-julian-day->time-tai #f "modified-julian-day->time-tai") + (modified-julian-day->time-utc #f "modified-julian-day->time-utc") + (time-monotonic->date #f "time-monotonic->date") + (time-monotonic->julian-day #f "time-monotonic->julian-day") + (time-monotonic->modified-julian-day #f "time-monotonic->modified-julian-day") + (time-monotonic->time-tai #f "time-monotonic->time-tai") + (time-monotonic->time-tai! #f "time-monotonic->time-tai!") + (time-monotonic->time-utc #f "time-monotonic->time-utc") + (time-monotonic->time-utc! #f "time-monotonic->time-utc!") + (time-tai->date #f "time-tai->date") + (time-tai->julian-day #f "time-tai->julian-day") + (time-tai->modified-julian-day #f "time-tai->modified-julian-day") + (time-tai->time-monotonic #f "time-tai->time-monotonic") + (time-tai->time-monotonic! #f "time-tai->time-monotonic!") + (time-tai->time-utc #f "time-tai->time-utc") + (time-tai->time-utc! #f "time-tai->time-utc!") + (time-utc->date #f "time-utc->date") + (time-utc->julian-day #f "time-utc->julian-day") + (time-utc->modified-julian-day #f "time-utc->modified-julian-day") + (time-utc->time-monotonic #f "time-utc->time-monotonic") + (time-utc->time-monotonic! #f "time-utc->time-monotonic!") + (time-utc->time-tai #f "time-utc->time-tai") + (time-utc->time-tai! #f "time-utc->time-tai!") + (date->string #f "date->string") + (string->date #f "string->date") +)] + +Care most be taken NOT to confuse the internal date structure with the +PLT Scheme @scheme[date]; they are not the same, and all procedures +from the SRFI library expect the former. + +@; ---------------------------------------- + +@srfi[23]{Error reporting mechanism} + +@redirect[23 '( + (error #f "error") +)] + +@in-core[] + +@; ---------------------------------------- + +@srfi[25]{Multi-dimensional Array Primitives} + +@redirect[25 '( + (array? #f "array-p") + (make-array #f "make-array") + (shape #f "shape") + (array #f "array") + (array-rank #f "array-rank") + (array-start #f "array-start") + (array-end #f "array-end") + (array-ref #f "array-ref") + (array-set! #f "array-set!") + (share-array #f "share-array") +)] + +@; ---------------------------------------- + +@srfi[26]{Notation for Specializing Parameters without Currying} + +@redirect[26 '( + (cut #t "cut") + (cute #t "cute") +)] + + +@; ---------------------------------------- + +@srfi[27]{Sources of Random Bits} + +@redirect[27 '( + (random-integer #f "random-integer") + (random-real #f "random-real") + (default-random-source #f "default-random-source") + (make-random-source #f "make-random-source") + (random-source? #f "random-source-p") + (random-source-state-ref #f "random-source-state-ref") + (random-source-state-ref! #f "random-source-state-ref") + (random-source-randomize! #f "random-source-randomize!") + (random-source-pseudo-randomize! #f "random-source-pseudo-randomize!") + (random-source-make-integers #f "random-source-make-integers") + (random-source-make-reals #f "random-source-make-reals") +)] + +@; ---------------------------------------- + +@srfi[28]{Basic Format Strings} + +@redirect[28 '( + (format #f "format") +)] + +@in-core[] + +@; ---------------------------------------- + +@srfi[29]{Localization} + +@redirect[29 '( + (current-language #f "current-language") + (current-country #f "current-country") + (current-locale-details #f "current-locale-details") + (declare-bundle! #f "declare-bundle!") + (store-bundle #f "store-bundle") + (load-bundle! #f "load-bundle!") + (localized-template #f "localized-template") +)] + +@; ---------------------------------------- + +@srfi[30]{Nested Multi-line Comments} + +This SRFI's syntax is part of PLT Scheme's default reader. + +@; ---------------------------------------- + +@srfi[31]{A special form rec for recursive evaluation} + +@redirect[31 '( + (rec #t "rec") +)] + +@; ---------------------------------------- + +@srfi[34]{Exception Handling for Programs} + +@redirect[34 '( + (with-exception-handler #f "with-exception-handler") + (guard #t "guard") + (raise #f "raise") +)] + +@; ---------------------------------------- + +@srfi[35]{Conditions} + +@; ---------------------------------------- + +@srfi[38]{External Representation for Data With Shared Structure} + +@redirect[38 '( + (write-with-shared-structure #f "write-with-shared-structure") + (read-with-shared-structure #f "read-with-shared-structure") +)] + +This SRFI's syntax is part of PLT Scheme's default reader and printer. + +@; ---------------------------------------- + +@srfi[39]{Parameter objects} + +@redirect[39 '( + (make-parameter #f "make-parameter") + (parameterize #t "parameterize") +)] + +@in-core[] + +@; ---------------------------------------- + +@srfi[40]{A Library of Streams} + +@redirect[40 '( + (stream-cons #t "stream-cons") + (stream? #f "stream?") + (stream-null? #f "stream-null?") + (stream-car #f "stream-car") + (stream-cdr #f "stream-cdr") + (stream-delay #t "stream-delay") + (stream-null #f "stream-null") + (stream #f "stream") + (stream-unfoldn #f "stream-unfoldn") + (stream-map #f "stream-map") + (stream-for-each #f "stream-for-each") + (stream-filter #f "stream-filter") +)] + +@; ---------------------------------------- + +@srfi[42]{Eager Comprehensions} + +@redirect[42 '( + (do-ec #t "do-ec") + (list-ec #t "list-ec") + (append-ec #t "append-ec") + (string-ec #t "string-ec") + (string-append-ec #t "string-append-ec") + (vector-ec #t "vector-ec") + (vector-of-length-ec #t "vector-of-length-ec") + (sum-ec #t "sum-ec") + (product-ec #t "product-ec") + (min-ec #t "min-ec") + (max-ec #t "max-ec") + (any?-ec #t "any?-ec") + (every?-ec #t "every?-ec") + (first-ec #t "first-ec") + (last-ec #t "last-ec") + (fold-ec #t "fold-ec") + (fold3-ec #t "fold3-ec") + (generator #t "generator") + (if #t "if") + (not #t "not") + (and #t "and") + (or #t "or") + (begin #t "begin") + (nested #t "nested") + (: #t ":") + (:list #t ":list") + (:string #t ":string") + (:vector #t ":vector") + (:integers #t ":integers") + (:range #t ":range") + (:real-range #t ":real-range") + (:char-range #t ":char-range") + (:port #t ":port") + (:dispatched #t "") + (:generator-proc #t ":generator-proc") + (:do #t ":do") + (:let #t ":let") + (:parallel #t ":parallel") + (:while #t ":while") + (:until #t ":until") +)] + +@; ---------------------------------------- + +@srfi[43]{Vector Library} + +@redirect[43 '( + (make-vector #f "make-vector") + (vector #f "vector") + (vector-unfold #f "vector-unfold") + (vector-unfold-right #f "vector-unfold-right") + (vector-copy #f "vector-copy") + (vector-reverse-copy #f "vector-reverse-copy") + (vector-append #f "vector-append") + (vector-concatenate #f "vector-concatenate") + (vector? #f "vector-p") + (vector-empty? #f "vector-empty?") + (vector= #f "vector-eq") + (vector-ref #f "vector-ref") + (vector-length #f "vector-length") + (vector-fold #f "vector-fold") + (vector-fold-right #f "vector-fold-right") + (vector-map #f "vector-map") + (vector-map! #f "vector-map-bang") + (vector-for-each #f "vector-for-each") + (vector-count #f "vector-count") + (vector-index #f "vector-index") + (vector-index-right #f "vector-index-right") + (vector-skip #f "vector-skip") + (vector-skip-right #f "vector-skip-right") + (vector-binary-search #f "vector-binary-search") + (vector-any #f "vector-any") + (vector-every #f "vector-every") + (vector-set! #f "vector-set-bang") + (vector-swap! #f "vector-swap-bang") + (vector-fill! #f "vector-fill-bang") + (vector-reverse! #f "vector-reverse-bang") + (vector-copy! #f "vector-copy-bang") + (vector-reverse-copy! #f "vector-reverse-copy-bang") + (vector->list #f "vector->list") + (reverse-vector->list #f "reverse-vector->list") + (list->vector #f "list->vector") + (reverse-list->vector #f "reverse-list->vector") +)] + +@; ---------------------------------------- + +@srfi[45]{Primitives for Expressing Iterative Lazy Algorithms} + +@redirect[45 '( + (delay #t "delay") + (lazy #t "lazy") + (force #f "force") + (eager #f "eager") +)] + +Additional binding: + +@defproc[(promise? [v any/c]) boolean?]{ +Returns @scheme[#t] if @scheme[v] is a promise, @scheme[#f] otherwise.} + +@; ---------------------------------------- + +@srfi[48]{Intermediate Format Strings} + +@redirect[48 '( + (format #f "format") +)] + +@; ---------------------------------------- + +@srfi[54]{Formatting} + +@; ---------------------------------------- + +@srfi[57]{Records} + +@; ---------------------------------------- + +@srfi[59]{Vicinity} + +@redirect[59 '( + (program-vicinity #f "program-vicinity") + (library-vicinity #f "library-vicinity") + (implementation-vicinity #f "implementation-vicinity") + (user-vicinity #f "user-vicinity") + (home-vicinity #f "home-vicinity") + (in-vicinity #f "in-vicinity") + (sub-vicinity #f "sub-vicinity") + (make-vicinity #f "make-vicinity") + (pathname->vicinity #f "pathname-to-vicinity") + (vicinity:suffix? #f "vicinity:suffix-p") +)] + +@; ---------------------------------------- + +@srfi[60]{Integers as Bits} + +@; ---------------------------------------- + +@srfi[61]{A more general cond clause} + +@; ---------------------------------------- + +@srfi[62]{S-expression comments} + +This SRFI's syntax is part of PLT Scheme's default reader. + +@; ---------------------------------------- + +@srfi[63]{Homogeneous and Heterogeneous Arrays} + +@; ---------------------------------------- + +@srfi[64]{A Scheme API for test suites} + +@; ---------------------------------------- + +@srfi[66]{Octet Vectors} + +@; ---------------------------------------- + +@srfi[67 #:subdir "srfi-67/"]{Compare Procedures} + +@; ---------------------------------------- + +@srfi[69]{Basic hash tables} + +@; ---------------------------------------- + +@srfi[71]{Extended LET-syntax for multiple values} + +@; ---------------------------------------- + +@srfi[74]{Octet-Addressed Binary Blocks} + +@; ---------------------------------------- + +@srfi[78]{Lightweight testing} + +@; ---------------------------------------- + +@srfi[86]{MU & NU simulating VALUES & CALL-WITH-VALUES...} + +@; ---------------------------------------- + +@srfi[87]{=> in case clauses} + +@; ---------------------------------------- + +@index-section[] diff --git a/doc/r5rs-std/index.html b/doc/r5rs-std/index.html new file mode 100644 index 0000000000..54b4a36d2a --- /dev/null +++ b/doc/r5rs-std/index.html @@ -0,0 +1,343 @@ + + + + + +Revised^5 Report on the Algorithmic Language Scheme + + + + + + + + + +

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+ +

+

+ + + + + + + + +

+

+

+

+ + + + + + +

+

+ +

+

+ +

+

+

+

+

+

+ +

+

+ + +

+

+ + + + + +

+

+

+

+

+

+ + + + + + + + + + +

+

+ + + + + + +

+

+ +

+

+ +

+

+

+

+ +

+

+ + + + +

+

+ + +

+

+ + +

+

+

+

+

+

+

+

+

+

+

+

+

+

+ +

+

+

+

+

+

+ +

+

+ +

+

+ +

+

+

+

+

+

+

+

+

+

+

+

+ +

+

+

+

+ +

+

+

+

+ +

+

+

+

+

+

+

+

+

+

+

+

+

+

+ +

+

+ + + + + + + +

+

+

+

+

+

+

+

+

+

+

+

+ +

+

+

+
+



Revised5 Report on the Algorithmic Language Scheme

+

+20 February 1998
+
+Richard Kelsey, William Clinger, and Jonathan Rees (Editors)

+ + + + + +
H. Abelson R. K. Dybvig C. T. Haynes G. J. Rozas
N. I. Adams IV D. P. Friedman E. Kohlbecker G. L. Steele Jr.
D. H. Bartley R. Halstead D. Oxley G. J. Sussman
G. Brooks C. Hanson K. M. Pitman M. Wand

+Dedicated to the Memory of Robert Hieb +

+

+

+

+

+ +

+

+ +

+

+ + +

+

+

+

+

+

+

+

+

+

+

+

+

+

+ + + +

+

+ +

+

+ + +

+

+ +

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+ +

+

+

+

+ + + + +

+

+ +

+

+

+ +

+ + diff --git a/doc/r5rs-std/r5rs-Z-C.css b/doc/r5rs-std/r5rs-Z-C.css new file mode 100644 index 0000000000..1fcd5a0082 --- /dev/null +++ b/doc/r5rs-std/r5rs-Z-C.css @@ -0,0 +1,102 @@ + + +body { + color: black; +/*background-color: #e5e5e5; */ +/*background-color: beige;*/ + margin-top: 2em; + margin-left: 8%; + margin-right: 8%; +} + +h1,h2,h3,h4,h5,h6 { + margin-top: .5em; +} + +.partheading { + font-size: 90%; +} + +.chapterheading { + font-size: 90%; +} + +pre { + margin-left: 2em; +} + +ol { + list-style-type: decimal; +} + +ol ol { + list-style-type: lower-alpha; +} + +ol ol ol { + list-style-type: lower-roman; +} + +ol ol ol ol { + list-style-type: upper-alpha; +} + +.scheme { + color: brown; +} + +.scheme .keyword { + color: #990000; + font-weight: bold; +} + +.scheme .builtin { + color: #990000; +} + +.scheme .variable { + color: navy; +} + +.scheme .global { + color: purple; +} + +.scheme .selfeval { + color: green; +} + +.scheme .comment { + color: teal; +} + +.schemeresponse { + color: green; +} + +.navigation { + color: red; + text-align: right; + font-style: italic; +} + +.disable { + /* color: #e5e5e5; */ +color: gray; +} + +.smallcaps { +font-size: 75%; +} + +.smallprint { + color: gray; + font-size: 75%; + text-align: right; +} + +.smallprint hr { + text-align: left; + width: 40%; +} + diff --git a/doc/r5rs-std/r5rs-Z-G-1.gif b/doc/r5rs-std/r5rs-Z-G-1.gif new file mode 100644 index 0000000000..8c4cff62f2 Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-1.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-10.gif b/doc/r5rs-std/r5rs-Z-G-10.gif new file mode 100644 index 0000000000..d4462b0b61 Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-10.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-11.gif b/doc/r5rs-std/r5rs-Z-G-11.gif new file mode 100644 index 0000000000..74e03cc2ea Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-11.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-12.gif b/doc/r5rs-std/r5rs-Z-G-12.gif new file mode 100644 index 0000000000..1c5d2181ae Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-12.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-13.gif b/doc/r5rs-std/r5rs-Z-G-13.gif new file mode 100644 index 0000000000..20fee34031 Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-13.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-14.gif b/doc/r5rs-std/r5rs-Z-G-14.gif new file mode 100644 index 0000000000..13555c259f Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-14.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-15.gif b/doc/r5rs-std/r5rs-Z-G-15.gif new file mode 100644 index 0000000000..580ed0fbaf Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-15.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-16.gif b/doc/r5rs-std/r5rs-Z-G-16.gif new file mode 100644 index 0000000000..6b3f5780ed Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-16.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-17.gif b/doc/r5rs-std/r5rs-Z-G-17.gif new file mode 100644 index 0000000000..f126fec48e Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-17.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-18.gif b/doc/r5rs-std/r5rs-Z-G-18.gif new file mode 100644 index 0000000000..0c9a00b8af Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-18.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-19.gif b/doc/r5rs-std/r5rs-Z-G-19.gif new file mode 100644 index 0000000000..3a3de5f36a Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-19.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-2.gif b/doc/r5rs-std/r5rs-Z-G-2.gif new file mode 100644 index 0000000000..ac8aaa3a5d Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-2.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-20.gif b/doc/r5rs-std/r5rs-Z-G-20.gif new file mode 100644 index 0000000000..d44b6584a4 Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-20.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-21.gif b/doc/r5rs-std/r5rs-Z-G-21.gif new file mode 100644 index 0000000000..af77639aac Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-21.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-22.gif b/doc/r5rs-std/r5rs-Z-G-22.gif new file mode 100644 index 0000000000..ce50fc766a Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-22.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-23.gif b/doc/r5rs-std/r5rs-Z-G-23.gif new file mode 100644 index 0000000000..22a2c5d9af Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-23.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-24.gif b/doc/r5rs-std/r5rs-Z-G-24.gif new file mode 100644 index 0000000000..131dc6f3af Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-24.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-25.gif b/doc/r5rs-std/r5rs-Z-G-25.gif new file mode 100644 index 0000000000..ce30d51177 Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-25.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-26.gif b/doc/r5rs-std/r5rs-Z-G-26.gif new file mode 100644 index 0000000000..40c5fb5626 Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-26.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-27.gif b/doc/r5rs-std/r5rs-Z-G-27.gif new file mode 100644 index 0000000000..d73589b88a Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-27.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-28.gif b/doc/r5rs-std/r5rs-Z-G-28.gif new file mode 100644 index 0000000000..56c3b2906b Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-28.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-29.gif b/doc/r5rs-std/r5rs-Z-G-29.gif new file mode 100644 index 0000000000..98c2fcb371 Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-29.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-3.gif b/doc/r5rs-std/r5rs-Z-G-3.gif new file mode 100644 index 0000000000..728e4afa63 Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-3.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-30.gif b/doc/r5rs-std/r5rs-Z-G-30.gif new file mode 100644 index 0000000000..1bcc918f40 Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-30.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-31.gif b/doc/r5rs-std/r5rs-Z-G-31.gif new file mode 100644 index 0000000000..ba6e494787 Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-31.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-32.gif b/doc/r5rs-std/r5rs-Z-G-32.gif new file mode 100644 index 0000000000..f5b2e6a95c Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-32.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-33.gif b/doc/r5rs-std/r5rs-Z-G-33.gif new file mode 100644 index 0000000000..ef2941fac6 Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-33.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-34.gif b/doc/r5rs-std/r5rs-Z-G-34.gif new file mode 100644 index 0000000000..6c62bd937f Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-34.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-35.gif b/doc/r5rs-std/r5rs-Z-G-35.gif new file mode 100644 index 0000000000..451eec50b4 Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-35.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-36.gif b/doc/r5rs-std/r5rs-Z-G-36.gif new file mode 100644 index 0000000000..1f8b92b390 Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-36.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-37.gif b/doc/r5rs-std/r5rs-Z-G-37.gif new file mode 100644 index 0000000000..5bd0331d3c Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-37.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-38.gif b/doc/r5rs-std/r5rs-Z-G-38.gif new file mode 100644 index 0000000000..838e3f8cdc Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-38.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-39.gif b/doc/r5rs-std/r5rs-Z-G-39.gif new file mode 100644 index 0000000000..65ad1b34eb Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-39.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-4.gif b/doc/r5rs-std/r5rs-Z-G-4.gif new file mode 100644 index 0000000000..a2d0862b02 Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-4.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-40.gif b/doc/r5rs-std/r5rs-Z-G-40.gif new file mode 100644 index 0000000000..db540e4a43 Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-40.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-41.gif b/doc/r5rs-std/r5rs-Z-G-41.gif new file mode 100644 index 0000000000..9695e63274 Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-41.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-42.gif b/doc/r5rs-std/r5rs-Z-G-42.gif new file mode 100644 index 0000000000..43c24a5fb4 Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-42.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-43.gif b/doc/r5rs-std/r5rs-Z-G-43.gif new file mode 100644 index 0000000000..4de6d466a3 Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-43.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-44.gif b/doc/r5rs-std/r5rs-Z-G-44.gif new file mode 100644 index 0000000000..3397dd644e Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-44.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-45.gif b/doc/r5rs-std/r5rs-Z-G-45.gif new file mode 100644 index 0000000000..6734d5aa4b Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-45.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-46.gif b/doc/r5rs-std/r5rs-Z-G-46.gif new file mode 100644 index 0000000000..026f48df74 Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-46.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-47.gif b/doc/r5rs-std/r5rs-Z-G-47.gif new file mode 100644 index 0000000000..62c9e3a603 Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-47.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-48.gif b/doc/r5rs-std/r5rs-Z-G-48.gif new file mode 100644 index 0000000000..e01b3e4129 Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-48.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-49.gif b/doc/r5rs-std/r5rs-Z-G-49.gif new file mode 100644 index 0000000000..1f5ab2c3ba Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-49.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-5.gif b/doc/r5rs-std/r5rs-Z-G-5.gif new file mode 100644 index 0000000000..71537ac25f Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-5.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-50.gif b/doc/r5rs-std/r5rs-Z-G-50.gif new file mode 100644 index 0000000000..5bfede64e9 Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-50.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-51.gif b/doc/r5rs-std/r5rs-Z-G-51.gif new file mode 100644 index 0000000000..2e7158eb0d Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-51.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-52.gif b/doc/r5rs-std/r5rs-Z-G-52.gif new file mode 100644 index 0000000000..53c45d922f Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-52.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-53.gif b/doc/r5rs-std/r5rs-Z-G-53.gif new file mode 100644 index 0000000000..86a7d2c07b Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-53.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-54.gif b/doc/r5rs-std/r5rs-Z-G-54.gif new file mode 100644 index 0000000000..532d8899f9 Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-54.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-55.gif b/doc/r5rs-std/r5rs-Z-G-55.gif new file mode 100644 index 0000000000..e5d941a02f Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-55.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-56.gif b/doc/r5rs-std/r5rs-Z-G-56.gif new file mode 100644 index 0000000000..478f8b2e62 Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-56.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-57.gif b/doc/r5rs-std/r5rs-Z-G-57.gif new file mode 100644 index 0000000000..5c25d76696 Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-57.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-58.gif b/doc/r5rs-std/r5rs-Z-G-58.gif new file mode 100644 index 0000000000..12482c6806 Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-58.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-59.gif b/doc/r5rs-std/r5rs-Z-G-59.gif new file mode 100644 index 0000000000..9fd459d0c4 Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-59.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-6.gif b/doc/r5rs-std/r5rs-Z-G-6.gif new file mode 100644 index 0000000000..03aae2afdc Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-6.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-60.gif b/doc/r5rs-std/r5rs-Z-G-60.gif new file mode 100644 index 0000000000..8c288fa2da Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-60.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-61.gif b/doc/r5rs-std/r5rs-Z-G-61.gif new file mode 100644 index 0000000000..47123a5756 Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-61.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-62.gif b/doc/r5rs-std/r5rs-Z-G-62.gif new file mode 100644 index 0000000000..8824232221 Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-62.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-63.gif b/doc/r5rs-std/r5rs-Z-G-63.gif new file mode 100644 index 0000000000..79910a58af Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-63.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-64.gif b/doc/r5rs-std/r5rs-Z-G-64.gif new file mode 100644 index 0000000000..aa2599e7e2 Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-64.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-65.gif b/doc/r5rs-std/r5rs-Z-G-65.gif new file mode 100644 index 0000000000..033e220208 Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-65.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-7.gif b/doc/r5rs-std/r5rs-Z-G-7.gif new file mode 100644 index 0000000000..c115b9e560 Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-7.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-8.gif b/doc/r5rs-std/r5rs-Z-G-8.gif new file mode 100644 index 0000000000..c937777ee8 Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-8.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-9.gif b/doc/r5rs-std/r5rs-Z-G-9.gif new file mode 100644 index 0000000000..7dc94c86f2 Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-9.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-D-1.gif b/doc/r5rs-std/r5rs-Z-G-D-1.gif new file mode 100644 index 0000000000..8c3df0afb0 Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-D-1.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-D-2.gif b/doc/r5rs-std/r5rs-Z-G-D-2.gif new file mode 100644 index 0000000000..9e20c170be Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-D-2.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-D-3.gif b/doc/r5rs-std/r5rs-Z-G-D-3.gif new file mode 100644 index 0000000000..0bff151b05 Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-D-3.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-D-4.gif b/doc/r5rs-std/r5rs-Z-G-D-4.gif new file mode 100644 index 0000000000..d7f29fd4a1 Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-D-4.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-D-5.gif b/doc/r5rs-std/r5rs-Z-G-D-5.gif new file mode 100644 index 0000000000..47ac67a375 Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-D-5.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-D-6.gif b/doc/r5rs-std/r5rs-Z-G-D-6.gif new file mode 100644 index 0000000000..45517d59a2 Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-D-6.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-D-7.gif b/doc/r5rs-std/r5rs-Z-G-D-7.gif new file mode 100644 index 0000000000..ed76e06b0a Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-D-7.gif differ diff --git a/doc/r5rs-std/r5rs-Z-G-D-8.gif b/doc/r5rs-std/r5rs-Z-G-D-8.gif new file mode 100644 index 0000000000..1004c1eb1c Binary files /dev/null and b/doc/r5rs-std/r5rs-Z-G-D-8.gif differ diff --git a/doc/r5rs-std/r5rs-Z-H-1.html b/doc/r5rs-std/r5rs-Z-H-1.html new file mode 100644 index 0000000000..7be7246c9b --- /dev/null +++ b/doc/r5rs-std/r5rs-Z-H-1.html @@ -0,0 +1,59 @@ + + + + + +Revised^5 Report on the Algorithmic Language Scheme + + + + + + +

+ +

+
 

+Summary

+

The report gives a defining description of the programming language +Scheme. Scheme is a statically scoped and properly tail-recursive +dialect of the Lisp programming language invented by Guy Lewis +Steele Jr. and Gerald Jay Sussman. It was designed to have an +exceptionally clear and simple semantics and few different ways to +form expressions. A wide variety of programming paradigms, including +imperative, functional, and message passing styles, find convenient +expression in Scheme.

+

+The introduction offers a brief history of the language and of +the report.

+

+The first three chapters present the fundamental ideas of the +language and describe the notational conventions used for describing the +language and for writing programs in the language.

+

+Chapters 4 and 5 describe +the syntax and semantics of expressions, programs, and definitions.

+

+Chapter 6 describes Scheme's built-in +procedures, which include all of the language's data manipulation and +input/output primitives.

+

+Chapter 7 provides a formal syntax for Scheme +written in extended BNF, along with a formal denotational semantics. +An example of the use of the language follows the formal syntax and +semantics.

+

+The report concludes with a list of references and an +alphabetic index.

+

+

+

+ +

+

+

+ +

+ + diff --git a/doc/r5rs-std/r5rs-Z-H-10.html b/doc/r5rs-std/r5rs-Z-H-10.html new file mode 100644 index 0000000000..1b6a642dc3 --- /dev/null +++ b/doc/r5rs-std/r5rs-Z-H-10.html @@ -0,0 +1,681 @@ + + + + + +Revised^5 Report on the Algorithmic Language Scheme + + + + + + +

+ +

+
Chapter 7

+Formal syntax and semantics

+

+

+This chapter provides formal descriptions of what has already been +described informally in previous chapters of this report.

+

+

+

+

+ +

7.1  Formal syntax

+

+

+This section provides a formal syntax for Scheme written in an extended +BNF.

+

+All spaces in the grammar are for legibility. Case is insignificant; +for example, #x1A and #X1a are equivalent. <empty> +stands for the empty string.

+

+The following extensions to BNF are used to make the description more +concise: <thing>* means zero or more occurrences of +<thing>; and <thing>+ means at least one +<thing>.

+

+

+ +

7.1.1  Lexical structure

+

This section describes how individual tokens (identifiers, +numbers, etc.) are formed from sequences of characters. The following +sections describe how expressions and programs are formed from sequences +of tokens.

+

+<Intertoken space> may occur on either side of any token, but not +within a token.

+

+Tokens which require implicit termination (identifiers, numbers, +characters, and dot) may be terminated by any <delimiter>, but not +necessarily by anything else.

+

+The following five characters are reserved for future extensions to the +language: [ ] { } |

+

+

+
+

+

+ +

+

+ +

+
+

+

+

+

+

+
+

+

+The following rules for <num R>, <complex R>, <real +R>, <ureal R>, <uinteger R>, and <prefix R> +should be replicated for R = 2, 8, 10, +and 16. There are no rules for <decimal 2>, <decimal +8>, and <decimal 16>, which means that numbers containing +decimal points or exponents must be in decimal radix. +

+

+

+
+

+

+

+
+

+

+

+

+

+ +

7.1.2  External representations

+

+

+<Datum> is what the read procedure (section 6.6.2) +successfully parses. Note that any string that parses as an +<expression> will also parse as a <datum>.

+

+

+
+

+

+

+ +

7.1.3  Expressions

+

+
+

+

+

+ +

7.1.4  Quasiquotations

+

The following grammar for quasiquote expressions is not context-free. +It is presented as a recipe for generating an infinite number of +production rules. Imagine a copy of the following rules for D = 1, 2, +3, .... D keeps track of the nesting depth.

+

+

+
+

+

+In <quasiquotation>s, a <list qq template D> can sometimes +be confused with either an <unquotation D> or a <splicing +unquotation D>. The interpretation as an +<unquotation> or <splicing +unquotation D> takes precedence.

+

+

+ +

7.1.5  Transformers

+

+
+

+

+

+ +

7.1.6  Programs and definitions

+

+
+

+        

+

+ +

7.2  Formal semantics

+

+

+

+

+ + + + + + + + + + + + + + + + + + + + + +

+

+ + + +This section provides a formal denotational semantics for the primitive +expressions of Scheme and selected built-in procedures. The concepts +and notation used here are described in [29]; the notation is +summarized below:

+

+

+
+

+

+The reason that expression continuations take sequences of values instead +of single values is to simplify the formal treatment of procedure calls +and multiple return values.

+

+The boolean flag associated with pairs, vectors, and strings will be true +for mutable objects and false for immutable objects.

+

+The order of evaluation within a call is unspecified. We mimic that +here by applying arbitrary permutations permute and unpermute, which must be inverses, to the arguments in a call before +and after they are evaluated. This is not quite right since it suggests, +incorrectly, that the order of evaluation is constant throughout a program (for +any given number of arguments), but it is a closer approximation to the intended +semantics than a left-to-right evaluation would be.

+

+The storage allocator new is implementation-dependent, but it must +obey the following axiom: if new L, then + (new | L) 2 = false.

+

+ + + +

+

+The definition of is omitted because an accurate definition of + would complicate the semantics without being very interesting.

+

+If P is a program in which all variables are defined before being +referenced or assigned, then the meaning of P is +

+
+

+where I* is the sequence of variables defined in P, P' +is the sequence of expressions obtained by replacing every definition +in P by an assignment, <undefined> is an expression that evaluates +to undefined, and + is the semantic function that assigns meaning to expressions.

+

+

+

+

+ +

7.2.1  Abstract syntax

+

+ + + + + + + +

+

+

+
+

+

+

+
+

+

+

+ +

7.2.2  Domain equations

+

+
+

+

+

+ +

7.2.3  Semantic functions

+

+ +

+

+

+
+

+

+ + + +

+

+

+

+

+

+

+

+Definition of deliberately omitted.

+

+

+
+

+

+

+
+

+

+

+
+

+

+

+
+

+

+

+
+

+

+

+
+

+

+

+
+

+

+

+
+

+

+Here and elsewhere, any expressed value other than undefined may +be used in place of unspecified.

+

+

+
+

+

+

+
+

+

+

+
+

+

+

+
+

+

+

+
+

+

+ +

+ +

7.2.4  Auxiliary functions

+

+

+

+
+

+

+

+
+

+

+

+
+

+

+

+
+

+

+

+
+

+

+

+
+

+

+

+
+

+

+

+
+

+

+

+
+

+

+

+
+

+

+

+
+

+

+

+
+

+

+

+
+

+

+

+
+

+

+

+
+

+

+

+
+

+

+

+
+

+

+

+
+

+

+

+
+

+

+

+
+

+

+

+
+

+

+ +

+
+

+

+ +

+
+

+

+ +

+
+

+

+

+
+

+

+ +

+
+

+

+ +

+
+

+

+ +

+
+

+

+

+
+

+

+

+
+

+

+

+
+

+

+

+
+

+

+ +        

+

+ +

7.3  Derived expression types

+

+

+This section gives macro definitions for the derived expression types in +terms of the primitive expression types (literal, variable, call, lambda, +if, set!). See section 6.4 for a possible +definition of delay.

+

+(define-syntax cond
+  (syntax-rules (else =>)
+    ((cond (else result1 result2 ...))
+     (begin result1 result2 ...))
+    ((cond (test => result))
+     (let ((temp test))
+       (if temp (result temp))))
+    ((cond (test => result) clause1 clause2 ...)
+     (let ((temp test))
+       (if temp
+           (result temp)
+           (cond clause1 clause2 ...))))
+    ((cond (test)) test)
+    ((cond (test) clause1 clause2 ...)
+     (let ((temp test))
+       (if temp
+           temp
+           (cond clause1 clause2 ...))))
+    ((cond (test result1 result2 ...))
+     (if test (begin result1 result2 ...)))
+    ((cond (test result1 result2 ...)
+           clause1 clause2 ...)
+     (if test
+         (begin result1 result2 ...)
+         (cond clause1 clause2 ...)))))
+

+ +

+(define-syntax case
+  (syntax-rules (else)
+    ((case (key ...)
+       clauses ...)
+     (let ((atom-key (key ...)))
+       (case atom-key clauses ...)))
+    ((case key
+       (else result1 result2 ...))
+     (begin result1 result2 ...))
+    ((case key
+       ((atoms ...) result1 result2 ...))
+     (if (memv key '(atoms ...))
+         (begin result1 result2 ...)))
+    ((case key
+       ((atoms ...) result1 result2 ...)
+       clause clauses ...)
+     (if (memv key '(atoms ...))
+         (begin result1 result2 ...)
+         (case key clause clauses ...)))))
+

+ +

+(define-syntax and
+  (syntax-rules ()
+    ((and) #t)
+    ((and test) test)
+    ((and test1 test2 ...)
+     (if test1 (and test2 ...) #f))))
+

+ +

+(define-syntax or
+  (syntax-rules ()
+    ((or) #f)
+    ((or test) test)
+    ((or test1 test2 ...)
+     (let ((x test1))
+       (if x x (or test2 ...))))))
+

+ +

+(define-syntax let
+  (syntax-rules ()
+    ((let ((name val) ...) body1 body2 ...)
+     ((lambda (name ...) body1 body2 ...)
+      val ...))
+    ((let tag ((name val) ...) body1 body2 ...)
+     ((letrec ((tag (lambda (name ...)
+                      body1 body2 ...)))
+        tag)
+      val ...))))
+

+ +

+(define-syntax let*
+  (syntax-rules ()
+    ((let* () body1 body2 ...)
+     (let () body1 body2 ...))
+    ((let* ((name1 val1) (name2 val2) ...)
+       body1 body2 ...)
+     (let ((name1 val1))
+       (let* ((name2 val2) ...)
+         body1 body2 ...)))))
+

+ +

+The following letrec macro uses the symbol <undefined> +in place of an expression which returns something that when stored in +a location makes it an error to try to obtain the value stored in the +location (no such expression is defined in Scheme). +A trick is used to generate the temporary names needed to avoid +specifying the order in which the values are evaluated. +This could also be accomplished by using an auxiliary macro.

+

+(define-syntax letrec
+  (syntax-rules ()
+    ((letrec ((var1 init1) ...) body ...)
+     (letrec "generate_temp_names"
+       (var1 ...)
+       ()
+       ((var1 init1) ...)
+       body ...))
+    ((letrec "generate_temp_names"
+       ()
+       (temp1 ...)
+       ((var1 init1) ...)
+       body ...)
+     (let ((var1 <undefined>) ...)
+       (let ((temp1 init1) ...)
+         (set! var1 temp1)
+         ...
+         body ...)))
+    ((letrec "generate_temp_names"
+       (x y ...)
+       (temp ...)
+       ((var1 init1) ...)
+       body ...)
+     (letrec "generate_temp_names"
+       (y ...)
+       (newtemp temp ...)
+       ((var1 init1) ...)
+       body ...))))
+

+ +

+(define-syntax begin
+  (syntax-rules ()
+    ((begin exp ...)
+     ((lambda () exp ...)))))
+

+ +

+The following alternative expansion for begin does not make use of +the ability to write more than one expression in the body of a lambda +expression. In any case, note that these rules apply only if the body +of the begin contains no definitions.

+

+(define-syntax begin
+  (syntax-rules ()
+    ((begin exp)
+     exp)
+    ((begin exp1 exp2 ...)
+     (let ((x exp1))
+       (begin exp2 ...)))))
+

+ +

+The following definition +of do uses a trick to expand the variable clauses. +As with letrec above, an auxiliary macro would also work. +The expression (if #f #f) is used to obtain an unspecific +value.

+

+(define-syntax do
+  (syntax-rules ()
+    ((do ((var init step ...) ...)
+         (test expr ...)
+         command ...)
+     (letrec
+       ((loop
+         (lambda (var ...)
+           (if test
+               (begin
+                 (if #f #f)
+                 expr ...)
+               (begin
+                 command
+                 ...
+                 (loop (do "step" var step ...)
+                       ...))))))
+       (loop init ...)))
+    ((do "step" x)
+     x)
+    ((do "step" x y)
+     y)))
+

+ +

+        

+

+

+ +

+ + diff --git a/doc/r5rs-std/r5rs-Z-H-11.html b/doc/r5rs-std/r5rs-Z-H-11.html new file mode 100644 index 0000000000..c0d4067da3 --- /dev/null +++ b/doc/r5rs-std/r5rs-Z-H-11.html @@ -0,0 +1,112 @@ + + + + + +Revised^5 Report on the Algorithmic Language Scheme + + + + + + +

+ +

+
 

+Notes

+

+

+

+

+

+

+

+

+ +

Language changes

+

+

+This section enumerates the changes that have been made to Scheme since +the ``Revised4 report'' [6] was published.

+

+

+ +

+

+ + +

+

+ +        

+

+

+ +

+ + diff --git a/doc/r5rs-std/r5rs-Z-H-12.html b/doc/r5rs-std/r5rs-Z-H-12.html new file mode 100644 index 0000000000..0a8b53c987 --- /dev/null +++ b/doc/r5rs-std/r5rs-Z-H-12.html @@ -0,0 +1,40 @@ + + + + + +Revised^5 Report on the Algorithmic Language Scheme + + + + + + +

+ +

+
 

+Additional material

+

+

+

+

+

+The Internet Scheme Repository at +

+
+ +http://www.cs.indiana.edu/scheme-repository/ +
+ +contains an extensive Scheme bibliography, as well as papers, +programs, implementations, and other material related to Scheme. + +

+ +

+ +

+ + diff --git a/doc/r5rs-std/r5rs-Z-H-13.html b/doc/r5rs-std/r5rs-Z-H-13.html new file mode 100644 index 0000000000..ebc6f0bf80 --- /dev/null +++ b/doc/r5rs-std/r5rs-Z-H-13.html @@ -0,0 +1,160 @@ + + + + + +Revised^5 Report on the Algorithmic Language Scheme + + + + + + +

+ +

+
 

+Example

+

+

+

+

+Integrate-system integrates the system +

+
+

+of differential equations with the method of Runge-Kutta.

+

+The parameter system-derivative is a function that takes a system +state (a vector of values for the state variables y1, ..., yn) +and produces a system derivative (the values y1/, ..., +yn/). The parameter initial-state provides an initial +system state, and h is an initial guess for the length of the +integration step.

+

+The value returned by integrate-system is an infinite stream of +system states.

+

+(define integrate-system
+  (lambda (system-derivative initial-state h)
+    (let ((next (runge-kutta-4 system-derivative h)))
+      (letrec ((states
+                (cons initial-state
+                      (delay (map-streams next
+                                          states)))))
+        states))))

+ +

+Runge-Kutta-4 takes a function, f, that produces a +system derivative from a system state. Runge-Kutta-4 +produces a function that takes a system state and +produces a new system state.

+

+(define runge-kutta-4
+  (lambda (f h)
+    (let ((*h (scale-vector h))
+          (*2 (scale-vector 2))
+          (*1/2 (scale-vector (/ 1 2)))
+          (*1/6 (scale-vector (/ 1 6))))
+      (lambda (y)
+        ;; y is a system state
+        (let* ((k0 (*h (f y)))
+               (k1 (*h (f (add-vectors y (*1/2 k0)))))
+               (k2 (*h (f (add-vectors y (*1/2 k1)))))
+               (k3 (*h (f (add-vectors y k2)))))
+          (add-vectors y
+            (*1/6 (add-vectors k0
+                               (*2 k1)
+                               (*2 k2)
+                               k3))))))))
+
+(define elementwise
+  (lambda (f)
+    (lambda vectors
+      (generate-vector
+        (vector-length (car vectors))
+        (lambda (i)
+          (apply f
+                 (map (lambda (v) (vector-ref  v i))
+                      vectors)))))))
+
+(define generate-vector
+  (lambda (size proc)
+    (let ((ans (make-vector size)))
+      (letrec ((loop
+                (lambda (i)
+                  (cond ((= i size) ans)
+                        (else
+                         (vector-set! ans i (proc i))
+                         (loop (+ i 1)))))))
+        (loop 0)))))
+
+(define add-vectors (elementwise +))
+
+(define scale-vector
+  (lambda (s)
+    (elementwise (lambda (x) (* x s)))))

+ +

+Map-streams is analogous to map: it applies its first +argument (a procedure) to all the elements of its second argument (a +stream).

+

+(define map-streams
+  (lambda (f s)
+    (cons (f (head s))
+          (delay (map-streams f (tail s))))))

+ +

+Infinite streams are implemented as pairs whose car holds the first +element of the stream and whose cdr holds a promise to deliver the rest +of the stream.

+

+(define head car)
+(define tail
+  (lambda (stream) (force (cdr stream))))

+ +

+

+


+


+

The following illustrates the use of integrate-system in +integrating the system +

+
+

+
+

+which models a damped oscillator.

+

+(define damped-oscillator
+  (lambda (R L C)
+    (lambda (state)
+      (let ((Vc (vector-ref state 0))
+            (Il (vector-ref state 1)))
+        (vector (- 0 (+ (/ Vc (* R C)) (/ Il C)))
+                (/ Vc L))))))
+
+(define the-states
+  (integrate-system
+     (damped-oscillator 10000 1000 .001)
+     '#(1 0)
+     .01))

+ +

+

+

+ +        

+

+ + + + + +

+ +

+ + diff --git a/doc/r5rs-std/r5rs-Z-H-14.html b/doc/r5rs-std/r5rs-Z-H-14.html new file mode 100644 index 0000000000..310359c6ff --- /dev/null +++ b/doc/r5rs-std/r5rs-Z-H-14.html @@ -0,0 +1,277 @@ + + + + + +Revised^5 Report on the Algorithmic Language Scheme + + + + + + +

+ +

+
 

+Bibliography

+

+ + +

+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
[1]   +

+Harold Abelson and Gerald Jay Sussman with Julie Sussman. +Structure and Interpretation of Computer Programs, second edition. +MIT Press, Cambridge, 1996.

+

+

+
[2]   +

Alan Bawden and Jonathan Rees. +Syntactic closures. +In Proceedings of the 1988 ACM Symposium on Lisp and +Functional Programming, pages 86-95.

+

+

+
[3]   +

+Robert G. Burger and R. Kent Dybvig. +Printing floating-point numbers quickly and accurately. +In Proceedings of the ACM SIGPLAN '96 Conference +on Programming Language Design and Implementation, pages 108-116.

+

+

+
[4]   +

+William Clinger, editor. +The revised revised report on Scheme, or an uncommon Lisp. +MIT Artificial Intelligence Memo 848, August 1985. +Also published as Computer Science Department Technical Report 174, +Indiana University, June 1985.

+

+

+
[5]   +

William Clinger. +How to read floating point numbers accurately. +In Proceedings of the ACM SIGPLAN '90 Conference +on Programming Language Design and Implementation, pages 92-101. +Proceedings published as SIGPLAN Notices 25(6), June 1990.

+

+

+
[6]   +

+William Clinger and Jonathan Rees, editors. +The revised4 report on the algorithmic language Scheme. +In ACM Lisp Pointers 4(3), pages 1-55, 1991.

+

+

+
[7]   +

William Clinger and Jonathan Rees. +Macros that work. +In Proceedings of the 1991 ACM Conference on Principles of +Programming Languages, pages 155-162.

+

+

+
[8]   +

William Clinger. +Proper Tail Recursion and Space Efficiency. +To appear in Proceedings of the 1998 ACM Conference on Programming +Language Design and Implementation, June 1998.

+

+

+
[9]   +

+R. Kent Dybvig, Robert Hieb, and Carl Bruggeman. +Syntactic abstraction in Scheme. +Lisp and Symbolic Computation 5(4):295-326, 1993.

+

+

+
[10]   +

+Carol Fessenden, William Clinger, Daniel P. Friedman, and Christopher Haynes. +Scheme 311 version 4 reference manual. +Indiana University Computer Science Technical Report 137, February 1983. +Superseded by [11].

+

+

+
[11]   +

+D. Friedman, C. Haynes, E. Kohlbecker, and M. Wand. +Scheme 84 interim reference manual. +Indiana University Computer Science Technical Report 153, January 1985.

+

+

+
[12]   +

+IEEE Standard 754-1985. IEEE Standard for Binary Floating-Point +Arithmetic. IEEE, New York, 1985.

+

+

+
[13]   +

+IEEE Standard 1178-1990. IEEE Standard for the Scheme +Programming Language. IEEE, New York, 1991.

+

+

+
[14]   +

+Eugene E. Kohlbecker Jr. +Syntactic Extensions in the Programming Language Lisp. +PhD thesis, Indiana University, August 1986.

+

+

+
[15]   +

+Eugene E. Kohlbecker Jr., Daniel P. Friedman, Matthias Felleisen, and Bruce Duba. +Hygienic macro expansion. +In Proceedings of the 1986 ACM Conference on Lisp +and Functional Programming, pages 151-161.

+

+

+
[16]   +

+Peter Landin. +A correspondence between Algol 60 and Church's lambda notation: Part I. +Communications of the ACM 8(2):89-101, February 1965.

+

+

+
[17]   +

+MIT Department of Electrical Engineering and Computer Science. +Scheme manual, seventh edition. +September 1984.

+

+

+
[18]   +

+Peter Naur et al. +Revised report on the algorithmic language Algol 60. +Communications of the ACM 6(1):1-17, January 1963.

+

+

+
[19]   +

+Paul Penfield, Jr. +Principal values and branch cuts in complex APL. +In APL '81 Conference Proceedings, pages 248-256. +ACM SIGAPL, San Francisco, September 1981. +Proceedings published as APL Quote Quad 12(1), ACM, September 1981.

+

+

+
[20]   +

+Kent M. Pitman. +The revised MacLisp manual (Saturday evening edition). +MIT Laboratory for Computer Science Technical Report 295, May 1983.

+

+

+
[21]   +

+Jonathan A. Rees and Norman I. Adams IV. +T: A dialect of Lisp or, lambda: The ultimate software tool. +In Conference Record of the 1982 ACM Symposium on Lisp and +Functional Programming, pages 114-122.

+

+

+
[22]   +

+Jonathan A. Rees, Norman I. Adams IV, and James R. Meehan. +The T manual, fourth edition. +Yale University Computer Science Department, January 1984.

+

+

+
[23]   +

+Jonathan Rees and William Clinger, editors. +The revised3 report on the algorithmic language Scheme. +In ACM SIGPLAN Notices 21(12), pages 37-79, December 1986.

+

+

+
[24]   +

+John Reynolds. +Definitional interpreters for higher order programming languages. +In ACM Conference Proceedings, pages 717-740. +ACM,  1972.

+

+

+
[25]   +

+Guy Lewis Steele Jr. and Gerald Jay Sussman. +The revised report on Scheme, a dialect of Lisp. +MIT Artificial Intelligence Memo 452, January 1978.

+

+

+
[26]   +

+Guy Lewis Steele Jr. +Rabbit: a compiler for Scheme. +MIT Artificial Intelligence Laboratory Technical Report 474, May 1978.

+

+

+
[27]   +

+Guy Lewis Steele Jr. +Common Lisp: The Language, second edition. +Digital Press, Burlington MA, 1990.

+

+

+
[28]   +

+Gerald Jay Sussman and Guy Lewis Steele Jr. +Scheme: an interpreter for extended lambda calculus. +MIT Artificial Intelligence Memo 349, December 1975.

+

+

+
[29]   +

+Joseph E. Stoy. +Denotational Semantics: The Scott-Strachey Approach to +Programming Language Theory. +MIT Press, Cambridge, 1977.

+

+

+
[30]   +

+Texas Instruments, Inc. +TI Scheme Language Reference Manual. +Preliminary version 1.0, November 1985.

+

+

+
+

+ +

+ + diff --git a/doc/r5rs-std/r5rs-Z-H-15.html b/doc/r5rs-std/r5rs-Z-H-15.html new file mode 100644 index 0000000000..5b23998c87 --- /dev/null +++ b/doc/r5rs-std/r5rs-Z-H-15.html @@ -0,0 +1,458 @@ + + + + + +Revised^5 Report on the Algorithmic Language Scheme + + + + + + +

+ +

+
 

+Alphabetic Index of Definitions of Concepts, +Keywords, and Procedures

+

+        

+

+

+

+

+ +

+

+

+

+
+
', [2]
+*
++, [2]
+,, [2]
+,@
+-, [2], +[3]
+...
+/, [2], +[3]
+;
+<, [2]
+<=
+=
+=>
+>
+>=
+`

+

+

+

+

+
+abs
+acos
+and
+angle
+append
+apply, [2]
+asin
+assoc
+assq
+assv
+atan, [2]

+

+

+

+

+
+#b
+backquote
+begin
+binding
+binding construct
+boolean?, [2]
+bound

+

+

+

+

+
+caar
+cadr
+call
+call by need
+call-with-current-continuation
+call-with-input-file
+call-with-output-file
+call-with-values
+car, [2]
+case
+cdddar
+cddddr
+cdr
+ceiling
+char->integer
+char-alphabetic?
+char-ci<=?
+char-ci<?
+char-ci=?
+char-ci>=?
+char-ci>?
+char-downcase
+char-lower-case?
+char-numeric?
+char-ready?, [2]
+char-upcase
+char-upper-case?
+char-whitespace?
+char<=?
+char<?
+char=?
+char>=?
+char>?
+char?, [2]
+close-input-port
+close-output-port
+combination
+comma
+comment
+complex?
+cond
+cons
+constant
+continuation
+cos
+current-input-port
+current-output-port

+

+

+

+

+
+#d
+define
+define-syntax
+definition
+delay
+denominator
+display, [2]
+do
+dotted pair
+dynamic-wind

+

+

+

+

+
+#e
+else, [2]
+empty list, [2], +[3], [4], +[5], [6], +[7]
+eof-object?
+eq?
+equal?
+equivalence predicate
+eqv?, [2]
+error
+escape procedure
+eval
+even?
+exact
+exact->inexact
+exact?
+exactness
+exp
+expt

+

+

+

+

+
+#f
+false, [2], +[3]
+floor
+for-each
+force

+

+

+

+

+
+gcd

+

+

+

+

+
+hygienic

+

+

+

+

+
+#i
+identifier, [2], +[3]
+if, [2]
+imag-part
+immutable
+implementation restriction, +[2]
+improper list
+inexact
+inexact->exact
+inexact?
+initial environment
+input-port?
+integer->char
+integer?
+interaction-environment
+internal definition

+

+

+

+

+
+keyword, [2]

+

+

+

+

+
+lambda
+lazy evaluation
+lcm
+length
+let, [2]
+let*
+let-syntax
+letrec
+letrec-syntax
+library
+library procedure
+list
+list->string
+list->vector
+list-ref
+list-tail
+list?
+load
+location
+log

+

+

+

+

+
+macro
+macro keyword
+macro transformer
+macro use
+magnitude
+make-polar
+make-rectangular
+make-string, [2]
+make-vector, [2]
+map
+max
+member
+memq
+memv
+min
+modulo
+mutable

+

+

+

+

+
+negative?
+newline, [2]
+not
+null-environment
+null?
+number
+number->string, +[2]
+number?, [2]
+numerator
+numerical types

+

+

+

+

+
+#o
+object
+odd?
+open-input-file
+open-output-file
+optional
+or
+output-port?

+

+

+

+

+
+pair
+pair?, [2]
+peek-char, [2]
+port
+port?
+positive?
+predicate
+procedure call
+procedure?, [2]
+promise, [2]
+proper tail recursion

+

+

+

+

+
+quasiquote
+quote
+quotient

+

+

+

+

+
+rational?
+rationalize
+read, [2]
+read-char, [2]
+real-part
+real?
+referentially transparent
+region, [2], +[3], [4], +[5], [6], +[7]
+remainder
+reverse
+round

+

+

+

+

+
+scheme-report-environment
+set!
+set-car!
+set-cdr!
+setcar
+simplest rational
+sin
+sqrt
+string
+string->list
+string->number, +[2]
+string->symbol
+string-append
+string-ci<=?
+string-ci<?
+string-ci=?
+string-ci>=?
+string-ci>?
+string-copy
+string-fill!
+string-length
+string-ref
+string-set!
+string<=?
+string<?
+string=?
+string>=?
+string>?
+string?, [2]
+substring
+symbol->string
+symbol?, [2]
+syntactic keyword, +[2], [3]
+syntax definition
+syntax-rules

+

+

+

+

+
+#t
+tail call
+tan
+token
+top level environment, +[2]
+transcript-off
+transcript-on
+true, [2], +[3], [4], +[5]
+truncate
+type

+

+

+

+

+
+unbound, [2], +[3]
+unspecified

+

+

+

+

+
+valid indexes, +[2]
+values
+variable, [2], +[3]
+vector
+vector->list
+vector-fill!
+vector-length
+vector-ref
+vector-set!
+vector?, [2]

+

+

+

+

+
+Whitespace
+with-input-from-file
+with-output-to-file
+write, [2]
+write-char, [2]

+

+

+

+

+
+#x

+

+

+

+

+
+zero?

+

+

+ + +

+

+ +

+ + diff --git a/doc/r5rs-std/r5rs-Z-H-2.html b/doc/r5rs-std/r5rs-Z-H-2.html new file mode 100644 index 0000000000..c292a0d715 --- /dev/null +++ b/doc/r5rs-std/r5rs-Z-H-2.html @@ -0,0 +1,146 @@ + + + + + +Revised^5 Report on the Algorithmic Language Scheme + + + + + + +

+ +

+
 

+Contents

+

+

+

+

+    Introduction
+            Background
+            Acknowledgements
+

+

+    1  Overview of Scheme
+        1.1  Semantics
+        1.2  Syntax
+        1.3  Notation and terminology
+            1.3.1  Primitive, library, and optional features
+            1.3.2  Error situations and unspecified behavior
+            1.3.3  Entry format
+            1.3.4  Evaluation examples
+            1.3.5  Naming conventions
+

+

+    2  Lexical conventions
+        2.1  Identifiers
+        2.2  Whitespace and comments
+        2.3  Other notations
+

+

+    3  Basic concepts
+        3.1  Variables, syntactic keywords, and regions
+        3.2  Disjointness of types
+        3.3  External representations
+        3.4  Storage model
+        3.5  Proper tail recursion
+

+

+    4  Expressions
+        4.1  Primitive expression types
+            4.1.1  Variable references
+            4.1.2  Literal expressions
+            4.1.3  Procedure calls
+            4.1.4  Procedures
+            4.1.5  Conditionals
+            4.1.6  Assignments
+        4.2  Derived expression types
+            4.2.1  Conditionals
+            4.2.2  Binding constructs
+            4.2.3  Sequencing
+            4.2.4  Iteration
+            4.2.5  Delayed evaluation
+            4.2.6  Quasiquotation
+        4.3  Macros
+            4.3.1  Binding constructs for syntactic keywords
+            4.3.2  Pattern language
+

+

+    5  Program structure
+        5.1  Programs
+        5.2  Definitions
+            5.2.1  Top level definitions
+            5.2.2  Internal definitions
+        5.3  Syntax definitions
+

+

+    6  Standard procedures
+        6.1  Equivalence predicates
+        6.2  Numbers
+            6.2.1  Numerical types
+            6.2.2  Exactness
+            6.2.3  Implementation restrictions
+            6.2.4  Syntax of numerical constants
+            6.2.5  Numerical operations
+            6.2.6  Numerical input and output
+        6.3  Other data types
+            6.3.1  Booleans
+            6.3.2  Pairs and lists
+            6.3.3  Symbols
+            6.3.4  Characters
+            6.3.5  Strings
+            6.3.6  Vectors
+        6.4  Control features
+        6.5  Eval
+        6.6  Input and output
+            6.6.1  Ports
+            6.6.2  Input
+            6.6.3  Output
+            6.6.4  System interface
+

+

+    7  Formal syntax and semantics
+        7.1  Formal syntax
+            7.1.1  Lexical structure
+            7.1.2  External representations
+            7.1.3  Expressions
+            7.1.4  Quasiquotations
+            7.1.5  Transformers
+            7.1.6  Programs and definitions
+        7.2  Formal semantics
+            7.2.1  Abstract syntax
+            7.2.2  Domain equations
+            7.2.3  Semantic functions
+            7.2.4  Auxiliary functions
+        7.3  Derived expression types
+

+

+    Notes
+            Language changes
+

+

+    Additional material
+

+

+    Example
+

+

+    Bibliography
+

+

+    Alphabetic Index of Definitions of Concepts, +Keywords, and Procedures
+

+

+

+

+ +

+ +

+ + diff --git a/doc/r5rs-std/r5rs-Z-H-3.html b/doc/r5rs-std/r5rs-Z-H-3.html new file mode 100644 index 0000000000..da92e2cca3 --- /dev/null +++ b/doc/r5rs-std/r5rs-Z-H-3.html @@ -0,0 +1,135 @@ + + + + + +Revised^5 Report on the Algorithmic Language Scheme + + + + + + +

+ +

+
 

+Introduction

+

+

+

+

+

+Programming languages should be designed not by piling feature on top of +feature, but by removing the weaknesses and restrictions that make additional +features appear necessary. Scheme demonstrates that a very small number +of rules for forming expressions, with no restrictions on how they are +composed, suffice to form a practical and efficient programming language +that is flexible enough to support most of the major programming +paradigms in use today.

+

+Scheme +was one of the first programming languages to incorporate first class +procedures as in the lambda calculus, thereby proving the usefulness of +static scope rules and block structure in a dynamically typed language. +Scheme was the first major dialect of Lisp to distinguish procedures +from lambda expressions and symbols, to use a single lexical +environment for all variables, and to evaluate the operator position +of a procedure call in the same way as an operand position. By relying +entirely on procedure calls to express iteration, Scheme emphasized the +fact that tail-recursive procedure calls are essentially goto's that +pass arguments. Scheme was the first widely used programming language to +embrace first class escape procedures, from which all previously known +sequential control structures can be synthesized. A subsequent +version of Scheme introduced the concept of exact and inexact numbers, +an extension of Common Lisp's generic arithmetic. +More recently, Scheme became the first programming language to support +hygienic macros, which permit the syntax of a block-structured language +to be extended in a consistent and reliable manner. + +

+

+

+ +

Background

+

The first description of Scheme was written in +1975 [28]. A revised report [25] + appeared in 1978, which described the evolution +of the language as its MIT implementation was upgraded to support an +innovative compiler [26]. Three distinct projects began in +1981 and 1982 to use variants of Scheme for courses at MIT, Yale, and +Indiana University [211710]. An introductory +computer science textbook using Scheme was published in +1984 [1].

+

+As Scheme became more widespread, +local dialects began to diverge until students and researchers +occasionally found it difficult to understand code written at other +sites. +Fifteen representatives of the major implementations of Scheme therefore +met in October 1984 to work toward a better and more widely accepted +standard for Scheme. +Their report [4] +was published at MIT and Indiana University in the summer of 1985. +Further revision took place in the spring of 1986 [23], +and in the spring of 1988 [6]. +The present report reflects further revisions agreed upon in a meeting +at Xerox PARC in June 1992.

+

+ +

+

+

+

+

+

+We intend this report to belong to the entire Scheme community, and so +we grant permission to copy it in whole or in part without fee. In +particular, we encourage implementors of Scheme to use this report as +a starting point for manuals and other documentation, modifying it as +necessary.

+

+

+ +

Acknowledgements

+

We would like to thank the following people for their help: Alan Bawden, Michael +Blair, George Carrette, Andy Cromarty, Pavel Curtis, Jeff Dalton, Olivier Danvy, +Ken Dickey, Bruce Duba, Marc Feeley, +Andy Freeman, Richard Gabriel, Yekta Gürsel, Ken Haase, Robert +Hieb, Paul Hudak, Morry Katz, Chris Lindblad, Mark Meyer, Jim Miller, Jim Philbin, +John Ramsdell, Mike Shaff, Jonathan Shapiro, Julie Sussman, +Perry Wagle, Daniel Weise, Henry Wu, and Ozan Yigit. +We thank Carol Fessenden, Daniel +Friedman, and Christopher Haynes for permission to use text from the Scheme 311 +version 4 reference manual. We thank Texas Instruments, Inc. for permission to +use text from the TI Scheme Language Reference Manual[30]. +We gladly acknowledge the influence of manuals for MIT Scheme[17], +T[22], Scheme 84[11],Common Lisp[27], +and Algol 60[18].

+

+We also thank Betty Dexter for the extreme effort she put into +setting this report in TEX, and Donald Knuth for designing the program +that caused her troubles.

+

+The Artificial Intelligence Laboratory of the +Massachusetts Institute of Technology, the Computer Science +Department of Indiana University, the Computer and Information +Sciences Department of the University of Oregon, and the NEC Research +Institute supported the preparation of this report. Support for the MIT +work was provided in part by +the Advanced Research Projects Agency of the Department of Defense under Office +of Naval Research contract N00014-80-C-0505. Support for the Indiana +University work was provided by NSF grants NCS 83-04567 and NCS +83-03325.

+

+

+

+

+

+ +

+ +

+ + diff --git a/doc/r5rs-std/r5rs-Z-H-4.html b/doc/r5rs-std/r5rs-Z-H-4.html new file mode 100644 index 0000000000..af75d2ba04 --- /dev/null +++ b/doc/r5rs-std/r5rs-Z-H-4.html @@ -0,0 +1,300 @@ + + + + + +Revised^5 Report on the Algorithmic Language Scheme + + + + + + +

+ +

+
Chapter 1

+Overview of Scheme

+

+ +

1.1  Semantics

+

+

+This section gives an overview of Scheme's semantics. A +detailed informal semantics is the subject of +chapters 3 through 6. For reference +purposes, section 7.2 provides a formal +semantics of Scheme.

+

+Following Algol, Scheme is a statically scoped programming +language. Each use of a variable is associated with a lexically +apparent binding of that variable.

+

+Scheme has latent as opposed to manifest types. Types +are associated with values (also called objects) rather than +with variables. (Some authors refer to languages with latent types as +weakly typed or dynamically typed languages.) Other languages with +latent types are APL, Snobol, and other dialects of Lisp. Languages +with manifest types (sometimes referred to as strongly typed or +statically typed languages) include Algol 60, Pascal, and C.

+

+All objects created in the course of a Scheme computation, including +procedures and continuations, have unlimited extent. +No Scheme object is ever destroyed. The reason that +implementations of Scheme do not (usually!) run out of storage is that +they are permitted to reclaim the storage occupied by an object if +they can prove that the object cannot possibly matter to any future +computation. Other languages in which most objects have unlimited +extent include APL and other Lisp dialects.

+

+Implementations of Scheme are required to be properly tail-recursive. +This allows the execution of an iterative computation in constant space, +even if the iterative computation is described by a syntactically +recursive procedure. Thus with a properly tail-recursive implementation, +iteration can be expressed using the ordinary procedure-call +mechanics, so that special iteration constructs are useful only as +syntactic sugar. See section 3.5.

+

+Scheme procedures are objects in their own right. Procedures can be +created dynamically, stored in data structures, returned as results of +procedures, and so on. Other languages with these properties include +Common Lisp and ML.

+

+One distinguishing feature of Scheme is that continuations, which +in most other languages only operate behind the scenes, also have +``first-class'' status. Continuations are useful for implementing a +wide variety of advanced control constructs, including non-local exits, +backtracking, and coroutines. See section 6.4.

+

+Arguments to Scheme procedures are always passed by value, which +means that the actual argument expressions are evaluated before the +procedure gains control, whether the procedure needs the result of the +evaluation or not. ML, C, and APL are three other languages that always +pass arguments by value. +This is distinct from the lazy-evaluation semantics of Haskell, +or the call-by-name semantics of Algol 60, where an argument +expression is not evaluated unless its value is needed by the +procedure.

+

+

+

+Scheme's model of arithmetic is designed to remain as independent as +possible of the particular ways in which numbers are represented within a +computer. In Scheme, every integer is a rational number, every rational is a +real, and every real is a complex number. Thus the distinction between integer +and real arithmetic, so important to many programming languages, does not +appear in Scheme. In its place is a distinction between exact arithmetic, +which corresponds to the mathematical ideal, and inexact arithmetic on +approximations. As in Common Lisp, exact arithmetic is not limited to +integers.

+

+

+ +

1.2  Syntax

+

Scheme, like most dialects of Lisp, employs a fully parenthesized prefix +notation for programs and (other) data; the grammar of Scheme generates a +sublanguage of the language used for data. An important +consequence of this simple, uniform representation is the susceptibility of +Scheme programs and data to uniform treatment by other Scheme programs. +For example, the eval procedure evaluates a Scheme program expressed +as data.

+

+The read procedure performs syntactic as well as lexical decomposition of +the data it reads. The read procedure parses its input as data +(section 7.1.2), not as program.

+

+The formal syntax of Scheme is described in section 7.1.

+

+

+ +

1.3  Notation and terminology

+

+ +

1.3.1  Primitive, library, and optional features

+

+

+It is required that every implementation of Scheme support all +features that are not marked as being optional. Implementations are +free to omit optional features of Scheme or to add extensions, +provided the extensions are not in conflict with the language reported +here. In particular, implementations must support portable code by +providing a syntactic mode that preempts no lexical conventions of this +report.

+

+To aid in understanding and implementing Scheme, some features are marked +as library. These can be easily implemented in terms of the other, +primitive, features. They are redundant in the strict sense of +the word, but they capture common patterns of usage, and are therefore +provided as convenient abbreviations.

+

+

+ +

1.3.2  Error situations and unspecified behavior

+

+When speaking of an error situation, this report uses the phrase ``an +error is signalled'' to indicate that implementations must detect and +report the error. If such wording does not appear in the discussion of +an error, then implementations are not required to detect or report the +error, though they are encouraged to do so. An error situation that +implementations are not required to detect is usually referred to simply +as ``an error.''

+

+For example, it is an error for a procedure to be passed an argument that +the procedure is not explicitly specified to handle, even though such +domain errors are seldom mentioned in this report. Implementations may +extend a procedure's domain of definition to include such arguments.

+

+This report uses the phrase ``may report a violation of an +implementation restriction'' to indicate circumstances under which an +implementation is permitted to report that it is unable to continue +execution of a correct program because of some restriction imposed by the +implementation. Implementation restrictions are of course discouraged, +but implementations are encouraged to report violations of implementation +restrictions.

+

+For example, an implementation may report a violation of an +implementation restriction if it does not have enough storage to run a +program.

+

+If the value of an expression is said to be ``unspecified,'' then +the expression must evaluate to some object without signalling an error, +but the value depends on the implementation; this report explicitly does +not say what value should be returned.

+

+

+

+

+

+

+ +

1.3.3  Entry format

+

Chapters 4 and 6 are organized +into entries. Each entry describes one language feature or a group of +related features, where a feature is either a syntactic construct or a +built-in procedure. An entry begins with one or more header lines of the form

+

+

+
category:  template 
+ +

+for required, primitive features, or

+

+

+
qualifier category:  template 
+ +

+where qualifier is either ``library'' or ``optional'' as defined +in section 1.3.1.

+

+If category is ``syntax'', the entry describes an expression +type, and the template gives the syntax of the expression type. +Components of expressions are designated by syntactic variables, which +are written using angle brackets, for example, <expression>, +<variable>. Syntactic variables should be understood to denote segments of +program text; for example, <expression> stands for any string of +characters which is a syntactically valid expression. The notation +

+

+        <thing1> ... +

+ +

+indicates zero or more occurrences of a <thing>, and +

+

+        <thing1> <thing2> ... +

+ +

+indicates one or more occurrences of a <thing>.

+

+If category is ``procedure'', then the entry describes a procedure, and +the header line gives a template for a call to the procedure. Argument +names in the template are italicized. Thus the header line

+

+

+
procedure:  (vector-ref vector k) 
+ +

+indicates that the built-in procedure vector-ref takes +two arguments, a vector vector and an exact non-negative integer +k (see below). The header lines

+

+

+
procedure:  (make-vector k) 
+ +
procedure:  (make-vector k fill) 
+ +

+indicate that the make-vector procedure must be defined to take +either one or two arguments.

+

+ +It is an error for an operation to be presented with an argument that it +is not specified to handle. For succinctness, we follow the convention +that if an argument name is also the name of a type listed in +section 3.2, then that argument must be of the named type. +For example, the header line for vector-ref given above dictates that the +first argument to vector-ref must be a vector. The following naming +conventions also imply type restrictions: + +

+
+ + + + + + + + + + +
objany object
list, list1, ... listj, ...list (see section 6.3.2)
z, z1, ... zj, ...complex number
x, x1, ... xj, ...real number
y, y1, ... yj, ...real number
q, q1, ... qj, ...rational number
n, n1, ... nj, ...integer
k, k1, ... kj, ...exact non-negative integer
+
+

+

+

+

+

+ +

1.3.4  Evaluation examples

+

The symbol ``===>'' used in program examples should be read +``evaluates to.'' For example,

+

+(* 5 8)              ===>  40

+ +

+means that the expression (* 5 8) evaluates to the object 40. +Or, more precisely: the expression given by the sequence of characters +``(* 5 8)'' evaluates, in the initial environment, to an object +that may be represented externally by the sequence of characters ``40''. See section 3.3 for a discussion of external +representations of objects.

+

+

+ +

1.3.5  Naming conventions

+

By convention, the names of procedures that always return a boolean +value usually end +in ``?''. Such procedures are called predicates.

+

+By convention, the names of procedures that store values into previously +allocated locations (see section 3.4) usually end in +``!''. +Such procedures are called mutation procedures. +By convention, the value returned by a mutation procedure is unspecified.

+

+By convention, ``->'' appears within the names of procedures that +take an object of one type and return an analogous object of another type. +For example, list->vector takes a list and returns a vector whose +elements are the same as those of the list.

+

+ +

+

+

+ +

+ + diff --git a/doc/r5rs-std/r5rs-Z-H-5.html b/doc/r5rs-std/r5rs-Z-H-5.html new file mode 100644 index 0000000000..c032fa15a7 --- /dev/null +++ b/doc/r5rs-std/r5rs-Z-H-5.html @@ -0,0 +1,201 @@ + + + + + +Revised^5 Report on the Algorithmic Language Scheme + + + + + + +

+ +

+
Chapter 2

+Lexical conventions

+

This section gives an informal account of some of the lexical +conventions used in writing Scheme programs. For a formal syntax of +Scheme, see section 7.1.

+

+Upper and lower case forms of a letter are never distinguished +except within character and string constants. For example, Foo is +the same identifier as FOO, and #x1AB is the same number as +#X1ab.

+

+

+ +

2.1  Identifiers

+

+

+Most identifiers allowed by other programming +languages are also acceptable to Scheme. The precise rules for forming +identifiers vary among implementations of Scheme, but in all +implementations a sequence of letters, digits, and ``extended alphabetic +characters'' that begins with a character that cannot begin a number is +an identifier. In addition, +, -, and ... are identifiers. +Here are some examples of identifiers:

+

+lambda                   q
+list->vector             soup
++                        V17a
+<=?                      a34kTMNs
+the-word-recursion-has-many-meanings

+ +

+Extended alphabetic characters may be used within identifiers as if +they were letters. The following are extended alphabetic characters:

+

+! $ % & * + - . / : < = > ? @ ^ _ ~ 

+ +

+See section 7.1.1 for a formal syntax of identifiers.

+

+Identifiers have two uses within Scheme programs: +

+ +

+

+

+

+

+ +

2.2  Whitespace and comments

+

Whitespace characters are spaces and newlines. +(Implementations typically provide additional whitespace characters such +as tab or page break.) Whitespace is used for improved readability and +as necessary to separate tokens from each other, a token being an +indivisible lexical unit such as an identifier or number, but is +otherwise insignificant. Whitespace may occur between any two tokens, +but not within a token. Whitespace may also occur inside a string, +where it is significant.

+

+A semicolon (;) indicates the start of a +comment. The comment continues to the +end of the line on which the semicolon appears. Comments are invisible +to Scheme, but the end of the line is visible as whitespace. This +prevents a comment from appearing in the middle of an identifier or +number.

+

+;;; The FACT procedure computes the factorial
+;;; of a non-negative integer.
+(define fact
+  (lambda (n)
+    (if (= n 0)
+        1        ;Base case: return 1
+        (* n (fact (- n 1))))))

+ +

+

+ +

2.3  Other notations

+

+

+For a description of the notations used for numbers, see +section 6.2.

+

+

+ +

+        

+

+ +

+ + diff --git a/doc/r5rs-std/r5rs-Z-H-6.html b/doc/r5rs-std/r5rs-Z-H-6.html new file mode 100644 index 0000000000..ba9413a7b7 --- /dev/null +++ b/doc/r5rs-std/r5rs-Z-H-6.html @@ -0,0 +1,338 @@ + + + + + +Revised^5 Report on the Algorithmic Language Scheme + + + + + + +

+ +

+
Chapter 3

+Basic concepts

+

+

+

+ +

3.1  Variables, syntactic keywords, and regions

+

+

+

+An identifier may name a type of syntax, or it may name +a location where a value can be stored. An identifier that names a type +of syntax is called a syntactic keyword +and is said to be bound to that syntax. An identifier that names a +location is called a variable and is said to be +bound to that location. The set of all visible +bindings in effect at some point in a program is +known as the environment in effect at that point. The value +stored in the location to which a variable is bound is called the +variable's value. By abuse of terminology, the variable is sometimes +said to name the value or to be bound to the value. This is not quite +accurate, but confusion rarely results from this practice.

+

+

+

+

+

+Certain expression types are used to create new kinds of syntax +and bind syntactic keywords to those new syntaxes, while other +expression types create new locations and bind variables to those +locations. These expression types are called binding constructs. + +Those that bind syntactic keywords are listed in section 4.3. +The most fundamental of the variable binding constructs is the +lambda expression, because all other variable binding constructs +can be explained in terms of lambda expressions. The other +variable binding constructs are let, let*, letrec, +and do expressions (see sections 4.1.4, 4.2.2, and +4.2.4).

+

+ +Like Algol and Pascal, and unlike most other dialects of Lisp +except for Common Lisp, Scheme is a statically scoped language with +block structure. To each place where an identifier is bound in a program +there corresponds a region of the program text within which +the binding is visible. The region is determined by the particular +binding construct that establishes the binding; if the binding is +established by a lambda expression, for example, then its region +is the entire lambda expression. Every mention of an identifier +refers to the binding of the identifier that established the +innermost of the regions containing the use. If there is no binding of +the identifier whose region contains the use, then the use refers to the +binding for the variable in the top level environment, if any +(chapters 4 and 6); if there is no +binding for the identifier, +it is said to be unbound.

+

+

+

+

+

+

+

+

+ +

3.2  Disjointness of types

+

+

+No object satisfies more than one of the following predicates:

+

+boolean?          pair?
+symbol?           number?
+char?             string?
+vector?           port?
+procedure?

+ +

+These predicates define the types boolean, pair, symbol, number, char (or character), string, vector, port, and procedure. The empty list is a special +object of its own type; it satisfies none of the above predicates. + + +

+

+Although there is a separate boolean type, +any Scheme value can be used as a boolean value for the purpose of a +conditional test. As explained in section 6.3.1, all +values count as true in such a test except for #f. +This report uses the word ``true'' to refer to any +Scheme value except #f, and the word ``false'' to refer to +#f.

+

+

+ +

3.3  External representations

+

+

+An important concept in Scheme (and Lisp) is that of the external +representation of an object as a sequence of characters. For example, +an external representation of the integer 28 is the sequence of +characters ``28'', and an external representation of a list consisting +of the integers 8 and 13 is the sequence of characters ``(8 13)''.

+

+The external representation of an object is not necessarily unique. The +integer 28 also has representations ``#e28.000'' and ``#x1c'', and the +list in the previous paragraph also has the representations ``( 08 13 +)'' and ``(8 . (13 . ()))'' (see section 6.3.2).

+

+Many objects have standard external representations, but some, such as +procedures, do not have standard representations (although particular +implementations may define representations for them).

+

+An external representation may be written in a program to obtain the +corresponding object (see quote, section 4.1.2).

+

+External representations can also be used for input and output. The +procedure read (section 6.6.2) parses external +representations, and the procedure write (section 6.6.3) +generates them. Together, they provide an elegant and powerful +input/output facility.

+

+Note that the sequence of characters ``(+ 2 6)'' is not an +external representation of the integer 8, even though it is an +expression evaluating to the integer 8; rather, it is an external +representation of a three-element list, the elements of which are the symbol ++ and the integers 2 and 6. Scheme's syntax has the property that +any sequence of characters that is an expression is also the external +representation of some object. This can lead to confusion, since it may +not be obvious out of context whether a given sequence of characters is +intended to denote data or program, but it is also a source of power, +since it facilitates writing programs such as interpreters and +compilers that treat programs as data (or vice versa).

+

+The syntax of external representations of various kinds of objects +accompanies the description of the primitives for manipulating the +objects in the appropriate sections of chapter 6.

+

+

+ +

3.4  Storage model

+

+

+Variables and objects such as pairs, vectors, and strings implicitly +denote locations or sequences of locations. A string, for +example, denotes as many locations as there are characters in the string. +(These locations need not correspond to a full machine word.) A new value may be +stored into one of these locations using the string-set! procedure, but +the string continues to denote the same locations as before.

+

+An object fetched from a location, by a variable reference or by +a procedure such as car, vector-ref, or string-ref, is +equivalent in the sense of eqv? (section 6.1) +to the object last stored in the location before the fetch.

+

+Every location is marked to show whether it is in use. +No variable or object ever refers to a location that is not in use. +Whenever this report speaks of storage being allocated for a variable +or object, what is meant is that an appropriate number of locations are +chosen from the set of locations that are not in use, and the chosen +locations are marked to indicate that they are now in use before the variable +or object is made to denote them.

+

+In many systems it is desirable for constants (i.e. the values of +literal expressions) to reside in read-only-memory. To express this, it is +convenient to imagine that every object that denotes locations is associated +with a flag telling whether that object is mutable or +immutable. In such systems literal constants and the strings +returned by symbol->string are immutable objects, while all objects +created by the other procedures listed in this report are mutable. It is an +error to attempt to store a new value into a location that is denoted by an +immutable object.

+

+

+ +

3.5  Proper tail recursion

+

+

+Implementations of Scheme are required to be +properly tail-recursive. +Procedure calls that occur in certain syntactic +contexts defined below are `tail calls'. A Scheme implementation is +properly tail-recursive if it supports an unbounded number of active +tail calls. A call is active if the called procedure may still +return. Note that this includes calls that may be returned from either +by the current continuation or by continuations captured earlier by +call-with-current-continuation that are later invoked. +In the absence of captured continuations, calls could +return at most once and the active calls would be those that had not +yet returned. +A formal definition of proper tail recursion can be found +in [8].

+

+

+
Rationale:   +

+Intuitively, no space is needed for an active tail call because the +continuation that is used in the tail call has the same semantics as the +continuation passed to the procedure containing the call. Although an improper +implementation might use a new continuation in the call, a return +to this new continuation would be followed immediately by a return +to the continuation passed to the procedure. A properly tail-recursive +implementation returns to that continuation directly.

+

+Proper tail recursion was one of the central ideas in Steele and +Sussman's original version of Scheme. Their first Scheme interpreter +implemented both functions and actors. Control flow was expressed using +actors, which differed from functions in that they passed their results +on to another actor instead of returning to a caller. In the terminology +of this section, each actor finished with a tail call to another actor.

+

+Steele and Sussman later observed that in their interpreter the code +for dealing with actors was identical to that for functions and thus +there was no need to include both in the language.

+

+

+
+

+A tail call is a procedure call that occurs +in a tail context. Tail contexts are defined inductively. Note +that a tail context is always determined with respect to a particular lambda +expression.

+

+

+ +

+

+Certain built-in procedures are also required to perform tail calls. +The first argument passed to apply and to +call-with-current-continuation, and the second argument passed to +call-with-values, must be called via a tail call. +Similarly, eval must evaluate its argument as if it +were in tail position within the eval procedure.

+

+In the following example the only tail call is the call to f. +None of the calls to g or h are tail calls. The reference to +x is in a tail context, but it is not a call and thus is not a +tail call. +(lambda ()
+  (if (g)
+      (let ((x (h)))
+        x)
+      (and (g) (f))))
+

+ +
Note:   +Implementations are allowed, but not required, to +recognize that some non-tail calls, such as the call to h +above, can be evaluated as though they were tail calls. +In the example above, the let expression could be compiled +as a tail call to h. (The possibility of h returning +an unexpected number of values can be ignored, because in that +case the effect of the let is explicitly unspecified and +implementation-dependent.) +
+

+        

+

+ +

+ + diff --git a/doc/r5rs-std/r5rs-Z-H-7.html b/doc/r5rs-std/r5rs-Z-H-7.html new file mode 100644 index 0000000000..75297f6b79 --- /dev/null +++ b/doc/r5rs-std/r5rs-Z-H-7.html @@ -0,0 +1,1201 @@ + + + + + +Revised^5 Report on the Algorithmic Language Scheme + + + + + + +

+ +

+
Chapter 4

+Expressions

+

+

+ +

+

+ +Expression types are categorized as primitive or derived. +Primitive expression types include variables and procedure calls. +Derived expression types are not semantically primitive, but can instead +be defined as macros. +With the exception of quasiquote, whose macro definition is complex, +the derived expressions are classified as library features. +Suitable definitions are given in section 7.3.

+

+

+ +

4.1  Primitive expression types

+

+

+

+ +

4.1.1  Variable references

+

+

+

+

+

+

+
syntax:  <variable> 
+ +

+An expression consisting of a variable +(section 3.1) is a variable reference. The value of +the variable reference is the value stored in the location to which the +variable is bound. It is an error to reference an +unbound variable.

+

+(define x 28)
+x           ===>  28

+ + +

+

+

+ +

4.1.2  Literal expressions

+

+

+

+

+

+

+
syntax:  (quote <datum>) 
+ +
syntax:  '<datum> 
+ +
syntax:  <constant> 
+ +

+(quote <datum>) evaluates to <datum>. +<Datum> +may be any external representation of a Scheme object (see +section 3.3). This notation is used to include literal +constants in Scheme code.

+

+(quote a)                             ===>  a
+(quote #(a b c))             ===>  #(a b c)
+(quote (+ 1 2))                       ===>  (+ 1 2)

+ +

+(quote <datum>) may be abbreviated as +'<datum>. The two notations are equivalent in all +respects.

+

+'a                           ===>  a
+'#(a b c)                   ===>  #(a b c)
+'()                          ===>  ()
+'(+ 1 2)                     ===>  (+ 1 2)
+'(quote a)                   ===>  (quote a)
+''a                          ===>  (quote a)

+ +

+Numerical constants, string constants, character constants, and boolean +constants evaluate ``to themselves''; they need not be quoted.

+

+'"abc"             ===>  "abc"
+"abc"              ===>  "abc"
+'145932            ===>  145932
+145932             ===>  145932
+'#t          ===>  #t
+#t           ===>  #t

+ +

+As noted in section 3.4, it is an error to alter a constant +(i.e. the value of a literal expression) using a mutation procedure like +set-car! or string-set!.

+

+

+ +

+

+

+ +

4.1.3  Procedure calls

+

+

+

+

+

+

+
syntax:  (<operator> <operand1> ...) 
+ +

+A procedure call is written by simply enclosing in parentheses +expressions for the procedure to be called and the arguments to be +passed to it. The operator and operand expressions are evaluated (in an +unspecified order) and the resulting procedure is passed the resulting +arguments. +(+ 3 4)                                  ===>  7
+((if #f + *) 3 4)                 ===>  12

+ +

+A number of procedures are available as the values of variables in the +initial environment; for example, the addition and multiplication +procedures in the above examples are the values of the variables + +and *. New procedures are created by evaluating lambda expressions +(see section 4.1.4). + + +Procedure calls may return any number of values (see values in +section 6.4). With the exception of values +the procedures available in the initial environment return one +value or, for procedures such as apply, pass on the values returned +by a call to one of their arguments.

+

+Procedure calls are also called combinations. +

+

+

+
Note:   In contrast to other dialects of Lisp, the order of +evaluation is unspecified, and the operator expression and the operand +expressions are always evaluated with the same evaluation rules. +
+

+

+
Note:   +Although the order of evaluation is otherwise unspecified, the effect of +any concurrent evaluation of the operator and operand expressions is +constrained to be consistent with some sequential order of evaluation. +The order of evaluation may be chosen differently for each procedure call. +
+

+

+
Note:   In many dialects of Lisp, the empty combination, (), is a legitimate expression. In Scheme, combinations must have at +least one subexpression, so () is not a syntactically valid +expression. +
+

+

+

+

+ +

+

+

+ +

4.1.4  Procedures

+

+

+

+

+

+

+
syntax:  (lambda <formals> <body>) 
+ +

+Syntax: <Formals> should be a formal arguments list as described below, +and <body> should be a sequence of one or more expressions.

+

+Semantics: A lambda expression evaluates to a procedure. The environment in +effect when the lambda expression was evaluated is remembered as part of the +procedure. When the procedure is later called with some actual +arguments, the environment in which the lambda expression was evaluated will +be extended by binding the variables in the formal argument list to +fresh locations, the corresponding actual argument values will be stored +in those locations, and the expressions in the body of the lambda expression +will be evaluated sequentially in the extended environment. +The result(s) of the last expression in the body will be returned as +the result(s) of the procedure call.

+

+(lambda (x) (+ x x))              ===>  a procedure
+((lambda (x) (+ x x)) 4)          ===>  8
+
+(define reverse-subtract
+  (lambda (x y) (- y x)))
+(reverse-subtract 7 10)                 ===>  3
+
+(define add4
+  (let ((x 4))
+    (lambda (y) (+ x y))))
+(add4 6)                                ===>  10

+ +

+<Formals> should have one of the following forms:

+

+

+ +

+

+It is an error for a <variable> to appear more than once in +<formals>.

+

+((lambda x x) 3 4 5 6)                  ===>  (3 4 5 6)
+((lambda (x y . z) z)
+ 3 4 5 6)                               ===>  (5 6)

+ +

+Each procedure created as the result of evaluating a lambda expression is +(conceptually) tagged +with a storage location, in order to make eqv? and +eq? work on procedures (see section 6.1).

+

+

+ +

+

+

+ +

4.1.5  Conditionals

+

+

+

+

+

+

+
syntax:  (if <test> <consequent> <alternate>) 
+ +
syntax:  (if <test> <consequent>) 
+ +Syntax: <Test>, <consequent>, and <alternate> may be arbitrary +expressions. +

+Semantics: An if expression is evaluated as follows: first, +<test> is evaluated. If it yields a true value (see +section 6.3.1), then <consequent> is evaluated and +its value(s) is(are) returned. Otherwise <alternate> is evaluated and its +value(s) is(are) returned. If <test> yields a false value and no +<alternate> is specified, then the result of the expression is +unspecified.

+

+(if (> 3 2) 'yes 'no)                   ===>  yes
+(if (> 2 3) 'yes 'no)                   ===>  no
+(if (> 3 2)
+    (- 3 2)
+    (+ 3 2))                            ===>  1

+ +

+

+ +

+

+

+ +

4.1.6  Assignments

+

+

+

+

+

+

+
syntax:  (set! <variable> <expression>) 
+ +

+<Expression> is evaluated, and the resulting value is stored in +the location to which <variable> is bound. <Variable> must +be bound either in some region enclosing the set! expression +or at top level. The result of the set! expression is +unspecified.

+

+(define x 2)
+(+ x 1)                         ===>  3
+(set! x 4)                      ===>  unspecified
+(+ x 1)                         ===>  5

+ +

+

+ +

+

+

+ +

4.2  Derived expression types

+

+

+The constructs in this section are hygienic, as discussed in +section 4.3. +For reference purposes, section 7.3 gives macro definitions +that will convert most of the constructs described in this section +into the primitive constructs described in the previous section.

+

+

+

+

+ +

4.2.1  Conditionals

+

+

+

+

+

+

+
library syntax:  (cond <clause1> <clause2> ...) 
+ +

+Syntax: Each <clause> should be of the form +(<test> <expression1...)

+ +where <test> is any expression. Alternatively, a <clause> may be +of the form +(<test> => <expression>) +The last <clause> may be +an ``else clause,'' which has the form +(else <expression1> <expression2...). + + +

+Semantics: A cond expression is evaluated by evaluating the <test> +expressions of successive <clause>s in order until one of them +evaluates to a true value (see +section 6.3.1). When a <test> evaluates to a true +value, then the remaining <expression>s in its <clause> are +evaluated in order, and the result(s) of the last <expression> in the +<clause> is(are) returned as the result(s) of the entire cond +expression. If the selected <clause> contains only the +<test> and no <expression>s, then the value of the +<test> is returned as the result. If the selected <clause> uses the +=> alternate form, then the <expression> is evaluated. +Its value must be a procedure that accepts one argument; this procedure is then +called on the value of the <test> and the value(s) returned by this +procedure is(are) returned by the cond expression. +If all <test>s evaluate +to false values, and there is no else clause, then the result of +the conditional expression is unspecified; if there is an else +clause, then its <expression>s are evaluated, and the value(s) of +the last one is(are) returned.

+

+(cond ((> 3 2) 'greater)
+      ((< 3 2) 'less))                 ===>  greater
+(cond ((> 3 3) 'greater)
+      ((< 3 3) 'less)
+      (else 'equal))                    ===>  equal
+(cond ((assv 'b '((a 1) (b 2))) => cadr)
+      (else #f))                 ===>  2

+ +

+

+ +

+

+

+

+
library syntax:  (case <key> <clause1> <clause2> ...) 
+ +

+Syntax: <Key> may be any expression. Each <clause> should have +the form +((<datum1...) <expression1> <expression2...),

+ +where each <datum> is an external representation of some object. +All the <datum>s must be distinct. +The last <clause> may be an ``else clause,'' which has the form +(else <expression1> <expression2...). + +

+Semantics: A case expression is evaluated as follows. <Key> is +evaluated and its result is compared against each <datum>. If the +result of evaluating <key> is equivalent (in the sense of +eqv?; see section 6.1) to a <datum>, then the +expressions in the corresponding <clause> are evaluated from left +to right and the result(s) of the last expression in the <clause> is(are) +returned as the result(s) of the case expression. If the result of +evaluating <key> is different from every <datum>, then if +there is an else clause its expressions are evaluated and the +result(s) of the last is(are) the result(s) of the case expression; +otherwise the result of the case expression is unspecified.

+

+(case (* 2 3)
+  ((2 3 5 7) 'prime)
+  ((1 4 6 8 9) 'composite))             ===>  composite
+(case (car '(c d))
+  ((a) 'a)
+  ((b) 'b))                             ===>  unspecified
+(case (car '(c d))
+  ((a e i o u) 'vowel)
+  ((w y) 'semivowel)
+  (else 'consonant))                    ===>  consonant

+ +

+

+ +

+

+

+

+
library syntax:  (and <test1> ...) 
+ +

+The <test> expressions are evaluated from left to right, and the +value of the first expression that evaluates to a false value (see +section 6.3.1) is returned. Any remaining expressions +are not evaluated. If all the expressions evaluate to true values, the +value of the last expression is returned. If there are no expressions +then #t is returned.

+

+(and (= 2 2) (> 2 1))                   ===>  #t
+(and (= 2 2) (< 2 1))                   ===>  #f
+(and 1 2 'c '(f g))                     ===>  (f g)
+(and)                                   ===>  #t

+ +

+

+ +

+

+

+

+
library syntax:  (or <test1> ...) 
+ +

+The <test> expressions are evaluated from left to right, and the value of the +first expression that evaluates to a true value (see +section 6.3.1) is returned. Any remaining expressions +are not evaluated. If all expressions evaluate to false values, the +value of the last expression is returned. If there are no +expressions then #f is returned.

+

+(or (= 2 2) (> 2 1))                    ===>  #t
+(or (= 2 2) (< 2 1))                    ===>  #t
+(or #f #f #f)         ===>  #f
+(or (memq 'b '(a b c)) 
+    (/ 3 0))                            ===>  (b c)

+ +

+

+ +

+

+

+ +

4.2.2  Binding constructs

+

The three binding constructs let, let*, and letrec +give Scheme a block structure, like Algol 60. The syntax of the three +constructs is identical, but they differ in the regions they establish +for their variable bindings. In a let expression, the initial +values are computed before any of the variables become bound; in a +let* expression, the bindings and evaluations are performed +sequentially; while in a letrec expression, all the bindings are in +effect while their initial values are being computed, thus allowing +mutually recursive definitions.

+

+

+

+
library syntax:  (let <bindings> <body>) 
+ +

+Syntax: <Bindings> should have the form +((<variable1> <init1>) ...),

+ +where each <init> is an expression, and <body> should be a +sequence of one or more expressions. It is +an error for a <variable> to appear more than once in the list of variables +being bound. +

+Semantics: The <init>s are evaluated in the current environment (in some +unspecified order), the <variable>s are bound to fresh locations +holding the results, the <body> is evaluated in the extended +environment, and the value(s) of the last expression of <body> +is(are) returned. Each binding of a <variable> has <body> as its +region.

+

+(let ((x 2) (y 3))
+  (* x y))                              ===>  6
+
+(let ((x 2) (y 3))
+  (let ((x 7)
+        (z (+ x y)))
+    (* z x)))                           ===>  35

+ +

+See also named let, section 4.2.4.

+

+

+ +

+

+

+

+
library syntax:  (let* <bindings> <body>) 
+ +

+Syntax: <Bindings> should have the form +((<variable1> <init1>) ...),

+ +and <body> should be a sequence of +one or more expressions. +

+Semantics: Let* is similar to let, but the bindings are performed +sequentially from left to right, and the region of a binding indicated +by (<variable> <init>) is that part of the let* +expression to the right of the binding. Thus the second binding is done +in an environment in which the first binding is visible, and so on.

+

+(let ((x 2) (y 3))
+  (let* ((x 7)
+         (z (+ x y)))
+    (* z x)))                     ===>  70

+ +

+

+ +

+

+

+

+
library syntax:  (letrec <bindings> <body>) 
+ +

+Syntax: <Bindings> should have the form +((<variable1> <init1>) ...),

+ +and <body> should be a sequence of +one or more expressions. It is an error for a <variable> to appear more +than once in the list of variables being bound. +

+Semantics: The <variable>s are bound to fresh locations holding undefined +values, the <init>s are evaluated in the resulting environment (in +some unspecified order), each <variable> is assigned to the result +of the corresponding <init>, the <body> is evaluated in the +resulting environment, and the value(s) of the last expression in +<body> is(are) returned. Each binding of a <variable> has the +entire letrec expression as its region, making it possible to +define mutually recursive procedures.

+

+(letrec ((even?
+          (lambda (n)
+            (if (zero? n)
+                #t
+                (odd? (- n 1)))))
+         (odd?
+          (lambda (n)
+            (if (zero? n)
+                #f
+                (even? (- n 1))))))
+  (even? 88))   
+                        ===>  #t

+ +

+One restriction on letrec is very important: it must be possible +to evaluate each <init> without assigning or referring to the value of any +<variable>. If this restriction is violated, then it is an error. The +restriction is necessary because Scheme passes arguments by value rather than by +name. In the most common uses of letrec, all the <init>s are +lambda expressions and the restriction is satisfied automatically.

+

+ +

+ +

+

+

+ +

4.2.3  Sequencing

+

+

+

+

+

+

+
library syntax:  (begin <expression1> <expression2> ...) 
+ +

+The <expression>s are evaluated sequentially from left to right, +and the value(s) of the last <expression> is(are) returned. This +expression type is used to sequence side effects such as input and +output.

+

+(define x 0)
+
+(begin (set! x 5)
+       (+ x 1))                          ===>  6
+
+(begin (display "4 plus 1 equals ")
+       (display (+ 4 1)))              ===>  unspecified
 and prints  4 plus 1 equals 5

+ +

+

+ +

+

+

+ +

4.2.4  Iteration

+

+

+
library syntax:  (do ((<variable1> <init1> <step1>) 
+ +     ...)
+   (<test> <expression> ...)
+ <command> ...)
+

+Do is an iteration construct. It specifies a set of variables to +be bound, how they are to be initialized at the start, and how they are +to be updated on each iteration. When a termination condition is met, +the loop exits after evaluating the <expression>s.

+

+Do expressions are evaluated as follows: +The <init> expressions are evaluated (in some unspecified order), +the <variable>s are bound to fresh locations, the results of the +<init> expressions are stored in the bindings of the +<variable>s, and then the iteration phase begins.

+

+Each iteration begins by evaluating <test>; if the result is +false (see section 6.3.1), then the <command> +expressions are evaluated in order for effect, the <step> +expressions are evaluated in some unspecified order, the +<variable>s are bound to fresh locations, the results of the +<step>s are stored in the bindings of the +<variable>s, and the next iteration begins.

+

+If <test> evaluates to a true value, then the +<expression>s are evaluated from left to right and the value(s) of +the last <expression> is(are) returned. If no <expression>s +are present, then the value of the do expression is unspecified.

+

+The region of the binding of a <variable> +consists of the entire do expression except for the <init>s. +It is an error for a <variable> to appear more than once in the +list of do variables.

+

+A <step> may be omitted, in which case the effect is the +same as if (<variable> <init> <variable>) had +been written instead of (<variable> <init>).

+

+(do ((vec (make-vector 5))
+     (i 0 (+ i 1)))
+    ((= i 5) vec)
+  (vector-set! vec i i))                  ===>  #(0 1 2 3 4)
+
+(let ((x '(1 3 5 7 9)))
+  (do ((x x (cdr x))
+       (sum 0 (+ sum (car x))))
+      ((null? x) sum)))                     ===>  25

+ +

+

+

+

+

+
library syntax:  (let <variable> <bindings> <body>) 
+ +

+ +``Named let'' is a variant on the syntax of let which provides +a more general looping construct than do and may also be used to express +recursions. +It has the same syntax and semantics as ordinary let +except that <variable> is bound within <body> to a procedure +whose formal arguments are the bound variables and whose body is +<body>. Thus the execution of <body> may be repeated by +invoking the procedure named by <variable>.

+

+(let loop ((numbers '(3 -2 1 6 -5))
+           (nonneg '())
+           (neg '()))
+  (cond ((null? numbers) (list nonneg neg))
+        ((>= (car numbers) 0)
+         (loop (cdr numbers)
+               (cons (car numbers) nonneg)
+               neg))
+        ((< (car numbers) 0)
+         (loop (cdr numbers)
+               nonneg
+               (cons (car numbers) neg)))))   
                ===>  ((6 1 3) (-5 -2))

+ +

+

+ +

+

+

+ +

4.2.5  Delayed evaluation

+

+

+

+

+

+

+
library syntax:  (delay <expression>) 
+ +

+

+

+The delay construct is used together with the procedure force to +implement lazy evaluation or call by need. +(delay <expression>) returns an object called a +promise which at some point in the future may be asked (by +the force procedure) to evaluate +<expression>, and deliver the resulting value. +The effect of <expression> returning multiple values +is unspecified.

+

+See the description of force (section 6.4) for a +more complete description of delay.

+

+

+ +

+

+

+ +

4.2.6  Quasiquotation

+

+

+

+

+

+

+
syntax:  (quasiquote <qq template>) 
+
syntax:  `<qq template> 
+ +

+``Backquote'' or ``quasiquote'' expressions are useful +for constructing a list or vector structure when most but not all of the +desired structure is known in advance. If no +commas appear within the <qq template>, the result of +evaluating +`<qq template> is equivalent to the result of evaluating +'<qq template>. If a comma appears within the +<qq template>, however, the expression following the comma is +evaluated (``unquoted'') and its result is inserted into the structure +instead of the comma and the expression. If a comma appears followed +immediately by an at-sign (@), then the following +expression must evaluate to a list; the opening and closing parentheses +of the list are then ``stripped away'' and the elements of the list are +inserted in place of the comma at-sign expression sequence. A comma +at-sign should only appear within a list or vector <qq template>.

+

+ +`(list ,(+ 1 2) 4)          ===>  (list 3 4)
+(let ((name 'a)) `(list ,name ',name))           
                ===>  (list a (quote a))
+`(a ,(+ 1 2) ,@(map abs '(4 -5 6)) b)           
                ===>  (a 3 4 5 6 b)
+`(( foo ,(- 10 3)) ,@(cdr '(c)) . ,(car '(cons)))           
                ===>  ((foo 7) . cons)
+`#(10 5 ,(sqrt 4) ,@(map sqrt '(16 9)) 8)           
                ===>  #(10 5 2 4 3 8)

+ +

+Quasiquote forms may be nested. Substitutions are made only for +unquoted components appearing at the same nesting level +as the outermost backquote. The nesting level increases by one inside +each successive quasiquotation, and decreases by one inside each +unquotation.

+

+`(a `(b ,(+ 1 2) ,(foo ,(+ 1 3) d) e) f)           
                ===>  (a `(b ,(+ 1 2) ,(foo 4 d) e) f)
+(let ((name1 'x)
+      (name2 'y))
+  `(a `(b ,,name1 ,',name2 d) e))           
                ===>  (a `(b ,x ,'y d) e)

+ +

+The two notations +`<qq template> and (quasiquote <qq template>) +are identical in all respects. +,<expression> is identical to (unquote <expression>), +and +,@<expression> is identical to (unquote-splicing <expression>). +The external syntax generated by write for two-element lists whose +car is one of these symbols may vary between implementations. +

+

+(quasiquote (list (unquote (+ 1 2)) 4))           
                ===>  (list 3 4)
+'(quasiquote (list (unquote (+ 1 2)) 4))           
                ===>  `(list ,(+ 1 2) 4)
+     i.e., (quasiquote (list (unquote (+ 1 2)) 4))

+ +

+Unpredictable behavior can result if any of the symbols +quasiquote, unquote, or unquote-splicing appear in +positions within a <qq template> otherwise than as described above.

+

+

+ +

+

+

+ +

4.3  Macros

+

+

+Scheme programs can define and use new derived expression types, +called macros. +Program-defined expression types have the syntax +(<keyword> <datum> ...)

+where <keyword> is an identifier that uniquely determines the +expression type. This identifier is called the syntactic +keyword, or simply keyword, of the macro. The +number of the <datum>s, and their syntax, depends on the +expression type. +

+Each instance of a macro is called a use +of the macro. +The set of rules that specifies +how a use of a macro is transcribed into a more primitive expression +is called the transformer +of the macro.

+

+The macro definition facility consists of two parts:

+

+

+ +

+

+The syntactic keyword of a macro may shadow variable bindings, and local +variable bindings may shadow keyword bindings. All macros +defined using the pattern language are ``hygienic'' and ``referentially +transparent'' and thus preserve Scheme's lexical scoping [1415279]: + +

+

+

+ +

+

+ + +

+ +

4.3.1  Binding constructs for syntactic keywords

+

+

+Let-syntax and letrec-syntax are +analogous to let and letrec, but they bind +syntactic keywords to macro transformers instead of binding variables +to locations that contain values. Syntactic keywords may also be +bound at top level; see section 5.3.

+

+

+

+
syntax:  (let-syntax <bindings> <body>) 
+ +

+Syntax: <Bindings> should have the form +((<keyword> <transformer spec>) ...)

+ +Each <keyword> is an identifier, +each <transformer spec> is an instance of syntax-rules, and +<body> should be a sequence of one or more expressions. It is an error +for a <keyword> to appear more than once in the list of keywords +being bound. +

+Semantics: The <body> is expanded in the syntactic environment +obtained by extending the syntactic environment of the +let-syntax expression with macros whose keywords are +the <keyword>s, bound to the specified transformers. +Each binding of a <keyword> has <body> as its region.

+

+(let-syntax ((when (syntax-rules ()
+                     ((when test stmt1 stmt2 ...)
+                      (if test
+                          (begin stmt1
+                                 stmt2 ...))))))
+  (let ((if #t))
+    (when if (set! if 'now))
+    if))                                   ===>  now
+
+(let ((x 'outer))
+  (let-syntax ((m (syntax-rules () ((m) x))))
+    (let ((x 'inner))
+      (m))))                               ===>  outer

+ +

+

+ +

+

+

+

+
syntax:  (letrec-syntax <bindings> <body>) 
+ +

+Syntax: Same as for let-syntax.

+

+Semantics: The <body> is expanded in the syntactic environment obtained by +extending the syntactic environment of the letrec-syntax +expression with macros whose keywords are the +<keyword>s, bound to the specified transformers. +Each binding of a <keyword> has the <bindings> +as well as the <body> within its region, +so the transformers can +transcribe expressions into uses of the macros +introduced by the letrec-syntax expression.

+

+(letrec-syntax
+  ((my-or (syntax-rules ()
+            ((my-or) #f)
+            ((my-or e) e)
+            ((my-or e1 e2 ...)
+             (let ((temp e1))
+               (if temp
+                   temp
+                   (my-or e2 ...)))))))
+  (let ((x #f)
+        (y 7)
+        (temp 8)
+        (let odd?)
+        (if even?))
+    (my-or x
+           (let temp)
+           (if y)
+           y)))                ===>  7

+ +

+

+ +

+

+

+ +

4.3.2  Pattern language

+

+

+A <transformer spec> has the following form:

+

+

+

+
:  (syntax-rules <literals> <syntax rule> ...) 
+ +

+Syntax: <Literals> is a list of identifiers and each <syntax rule> +should be of the form +(<pattern> <template>)

+ +The <pattern> in a <syntax rule> is a list <pattern> +that begins with the keyword for the macro. +

+A <pattern> is either an identifier, a constant, or one of the +following +(<pattern> ...)
+(<pattern> <pattern> ... . <pattern>)
+(<pattern> ... <pattern> <ellipsis>)
+#(<pattern> ...)
+#(<pattern> ... <pattern> <ellipsis>)

+ +and a template is either an identifier, a constant, or one of the following +(<element> ...)
+(<element> <element> ... . <template>)
+#(<element> ...)
+where an <element> is a <template> optionally +followed by an <ellipsis> and +an <ellipsis> is the identifier ``...'' (which cannot be used as +an identifier in either a template or a pattern). +

+Semantics: An instance of syntax-rules produces a new macro +transformer by specifying a sequence of hygienic rewrite rules. A use +of a macro whose keyword is associated with a transformer specified by +syntax-rules is matched against the patterns contained in the +<syntax rule>s, beginning with the leftmost <syntax rule>. +When a match is found, the macro use is transcribed hygienically +according to the template.

+

+An identifier that appears in the pattern of a <syntax rule> is +a pattern variable, unless it is the keyword that begins the pattern, +is listed in <literals>, or is the identifier ``...''. +Pattern variables match arbitrary input elements and +are used to refer to elements of the input in the template. It is an +error for the same pattern variable to appear more than once in a +<pattern>.

+

+The keyword at the beginning of the pattern in a +<syntax rule> is not involved in the matching and +is not considered a pattern variable or literal identifier.

+

+

+
Rationale:   +The scope of the keyword is determined by the expression or syntax +definition that binds it to the associated macro transformer. +If the keyword were a pattern variable or literal +identifier, then +the template that follows the pattern would be within its scope +regardless of whether the keyword were bound by let-syntax +or by letrec-syntax. +
+

+Identifiers that appear in <literals> are interpreted as literal +identifiers to be matched against corresponding subforms of the input. +A subform +in the input matches a literal identifier if and only if it is an +identifier +and either both its occurrence in the macro expression and its +occurrence in the macro definition have the same lexical binding, or +the two identifiers are equal and both have no lexical binding.

+

+ +A subpattern followed by ... can match zero or more elements of the +input. It is an error for ... to appear in <literals>. +Within a pattern the identifier ... must follow the last element of +a nonempty sequence of subpatterns.

+

+More formally, an input form F matches a pattern P if and only if:

+

+

+ +

+

+It is an error to use a macro keyword, within the scope of its +binding, in an expression that does not match any of the patterns.

+

+When a macro use is transcribed according to the template of the +matching <syntax rule>, pattern variables that occur in the +template are replaced by the subforms they match in the input. +Pattern variables that occur in subpatterns followed by one or more +instances of the identifier +... are allowed only in subtemplates that are +followed by as many instances of .... +They are replaced in the +output by all of the subforms they match in the input, distributed as +indicated. It is an error if the output cannot be built up as +specified.

+

+ +Identifiers that appear in the template but are not pattern variables +or the identifier +... are inserted into the output as literal identifiers. If a +literal identifier is inserted as a free identifier then it refers to the +binding of that identifier within whose scope the instance of +syntax-rules appears. +If a literal identifier is inserted as a bound identifier then it is +in effect renamed to prevent inadvertent captures of free identifiers.

+

+As an example, if let and cond are defined as in +section 7.3 then they are hygienic (as required) and +the following is not an error.

+

+(let ((=> #f))
+  (cond (#t => 'ok)))                   ===> ok

+ +

+The macro transformer for cond recognizes => +as a local variable, and hence an expression, and not as the +top-level identifier =>, which the macro transformer treats +as a syntactic keyword. Thus the example expands into

+

+(let ((=> #f))
+  (if #t (begin => 'ok)))

+ +

+instead of

+

+(let ((=> #f))
+  (let ((temp #t))
+    (if temp ('ok temp))))

+ +

+which would result in an invalid procedure call.

+

+

+ +

+

+        

+

+ +

+ + diff --git a/doc/r5rs-std/r5rs-Z-H-8.html b/doc/r5rs-std/r5rs-Z-H-8.html new file mode 100644 index 0000000000..05eec2cffc --- /dev/null +++ b/doc/r5rs-std/r5rs-Z-H-8.html @@ -0,0 +1,213 @@ + + + + + +Revised^5 Report on the Algorithmic Language Scheme + + + + + + +

+ +

+
Chapter 5

+Program structure

+

+

+

+ +

5.1  Programs

+

A Scheme program consists of a sequence of expressions, definitions, +and syntax definitions. +Expressions are described in chapter 4; +definitions and syntax definitions are the subject of the rest of the +present chapter.

+

+Programs are typically stored in files or entered interactively to a +running Scheme system, although other paradigms are possible; +questions of user interface lie outside the scope of this report. +(Indeed, Scheme would still be useful as a notation for expressing +computational methods even in the absence of a mechanical +implementation.)

+

+Definitions and syntax definitions occurring at the top level of a program +can be interpreted +declaratively. +They cause bindings to be created in the top level +environment or modify the value of existing top-level bindings. +Expressions occurring at the top level of a program are +interpreted imperatively; they are executed in order when the program is +invoked or loaded, and typically perform some kind of initialization.

+

+At the top level of a program (begin <form1> ...) is +equivalent to the sequence of expressions, definitions, and syntax definitions +that form the body of the begin.

+

+

+

+

+ +

5.2  Definitions

+

+

+Definitions are valid in some, but not all, contexts where expressions +are allowed. They are valid only at the top level of a <program> +and at the beginning of a <body>. +

+

+A definition should have one of the following forms:

+

+

+ +

+

+

+ +

5.2.1  Top level definitions

+

At the top level of a program, a definition +(define <variable> <expression>)

+ +has essentially the same effect as the assignment expression +(set! <variable> <expression>) +if <variable> is bound. If <variable> is not bound, +however, then the definition will bind <variable> to a new +location before performing the assignment, whereas it would be an error +to perform a set! on an unbound variable. +

+(define add3
+  (lambda (x) (+ x 3)))
+(add3 3)                                    ===>  6
+(define first car)
+(first '(1 2))                              ===>  1

+ +

+Some implementations of Scheme use an initial environment in +which all possible variables are bound to locations, most of +which contain undefined values. Top level definitions in +such an implementation are truly equivalent to assignments.

+

+

+

+

+ +

5.2.2  Internal definitions

+

+

+Definitions may occur at the +beginning of a <body> (that is, the body of a lambda, +let, let*, letrec, let-syntax, or letrec-syntax +expression or that of a definition of an appropriate form). +Such definitions are known as internal definitions as opposed to the top level definitions described above. +The variable defined by an internal definition is local to the +<body>. That is, <variable> is bound rather than assigned, +and the region of the binding is the entire <body>. For example,

+

+(let ((x 5))
+  (define foo (lambda (y) (bar x y)))
+  (define bar (lambda (a b) (+ (* a b) a)))
+  (foo (+ x 3)))                        ===>  45

+ +

+A <body> containing internal definitions can always be converted +into a completely equivalent letrec expression. For example, the +let expression in the above example is equivalent to

+

+(let ((x 5))
+  (letrec ((foo (lambda (y) (bar x y)))
+           (bar (lambda (a b) (+ (* a b) a))))
+    (foo (+ x 3))))

+ +

+Just as for the equivalent letrec expression, it must be +possible to evaluate each <expression> of every internal +definition in a <body> without assigning or referring to +the value of any <variable> being defined.

+

+Wherever an internal definition may occur +(begin <definition1> ...) +is equivalent to the sequence of definitions +that form the body of the begin.

+

+

+ +

5.3  Syntax definitions

+

Syntax definitions are valid only at the top level of a <program>. + +They have the following form:

+

+(define-syntax <keyword> <transformer spec>)

+

+<Keyword> is an identifier, and +the <transformer spec> should be an instance of syntax-rules. +The top-level syntactic environment is extended by binding the +<keyword> to the specified transformer.

+

+There is no define-syntax analogue of internal definitions.

+

+ + +Although macros may expand into definitions and syntax definitions in +any context that permits them, it is an error for a definition or syntax +definition to shadow a syntactic keyword whose meaning is needed to +determine whether some form in the group of forms that contains the +shadowing definition is in fact a definition, or, for internal definitions, +is needed to determine the boundary between the group and the expressions +that follow the group. For example, the following are errors:

+

+(define define 3)
+
+(begin (define begin list))
+
+(let-syntax
+  ((foo (syntax-rules ()
+          ((foo (proc args ...) body ...)
+           (define proc
+             (lambda (args ...)
+               body ...))))))
+  (let ((x 3))
+    (foo (plus x y) (+ x y))
+    (define foo x)
+    (plus foo x)))
+

+ +

+        

+

+

+ +

+ + diff --git a/doc/r5rs-std/r5rs-Z-H-9.html b/doc/r5rs-std/r5rs-Z-H-9.html new file mode 100644 index 0000000000..39bd296479 --- /dev/null +++ b/doc/r5rs-std/r5rs-Z-H-9.html @@ -0,0 +1,3484 @@ + + + + + +Revised^5 Report on the Algorithmic Language Scheme + + + + + + +

+ +

+
Chapter 6

+Standard procedures

+

+

+

+ + +

+

+This chapter describes Scheme's built-in procedures. The initial (or +``top level'') Scheme environment starts out with a number of variables +bound to locations containing useful values, most of which are primitive +procedures that manipulate data. For example, the variable abs is +bound to (a location initially containing) a procedure of one argument +that computes the absolute value of a number, and the variable + +is bound to a procedure that computes sums. Built-in procedures that +can easily be written in terms of other built-in procedures are identified as +``library procedures''.

+

+A program may use a top-level definition to bind any variable. It may +subsequently alter any such binding by an assignment (see 4.1.6). +These operations do not modify the behavior of Scheme's built-in +procedures. Altering any top-level binding that has not been introduced by a +definition has an unspecified effect on the behavior of the built-in procedures.

+

+

+ +

6.1  Equivalence predicates

+

+

+A predicate is a procedure that always returns a boolean +value (#t or #f). An equivalence predicate is +the computational analogue of a mathematical equivalence relation (it is +symmetric, reflexive, and transitive). Of the equivalence predicates +described in this section, eq? is the finest or most +discriminating, and equal? is the coarsest. Eqv? is +slightly less discriminating than eq?.

+

+

+

+
procedure:  (eqv? obj1 obj2) 
+ +

+The eqv? procedure defines a useful equivalence relation on objects. +Briefly, it returns #t if obj1 and obj2 should +normally be regarded as the same object. This relation is left slightly +open to interpretation, but the following partial specification of +eqv? holds for all implementations of Scheme.

+

+The eqv? procedure returns #t if:

+

+

+ +

+

+The eqv? procedure returns #f if:

+

+

+ +

+

+(eqv? 'a 'a)                             ===>  #t
+(eqv? 'a 'b)                             ===>  #f
+(eqv? 2 2)                               ===>  #t
+(eqv? '() '())                           ===>  #t
+(eqv? 100000000 100000000)               ===>  #t
+(eqv? (cons 1 2) (cons 1 2))             ===>  #f
+(eqv? (lambda () 1)
+      (lambda () 2))                     ===>  #f
+(eqv? #f 'nil)                          ===>  #f
+(let ((p (lambda (x) x)))
+  (eqv? p p))                                 ===>  #t

+ +

+The following examples illustrate cases in which the above rules do +not fully specify the behavior of eqv?. All that can be said +about such cases is that the value returned by eqv? must be a +boolean.

+

+(eqv? "" "")                     ===>  unspecified
+(eqv? '#() '#())                 ===>  unspecified
+(eqv? (lambda (x) x)
+      (lambda (x) x))            ===>  unspecified
+(eqv? (lambda (x) x)
+      (lambda (y) y))            ===>  unspecified

+ +

+The next set of examples shows the use of eqv? with procedures +that have local state. Gen-counter must return a distinct +procedure every time, since each procedure has its own internal counter. +Gen-loser, however, returns equivalent procedures each time, since +the local state does not affect the value or side effects of the +procedures.

+

+(define gen-counter
+  (lambda ()
+    (let ((n 0))
+      (lambda () (set! n (+ n 1)) n))))
+(let ((g (gen-counter)))
+  (eqv? g g))                   ===>  #t
+(eqv? (gen-counter) (gen-counter))
+                                ===>  #f
+(define gen-loser
+  (lambda ()
+    (let ((n 0))
+      (lambda () (set! n (+ n 1)) 27))))
+(let ((g (gen-loser)))
+  (eqv? g g))                   ===>  #t
+(eqv? (gen-loser) (gen-loser))
+                                ===>  unspecified
+
+(letrec ((f (lambda () (if (eqv? f g) 'both 'f)))
+         (g (lambda () (if (eqv? f g) 'both 'g))))
+  (eqv? f g))
+                                ===>  unspecified
+
+(letrec ((f (lambda () (if (eqv? f g) 'f 'both)))
+         (g (lambda () (if (eqv? f g) 'g 'both))))
+  (eqv? f g))
+                                ===>  #f

+ +

+ +Since it is an error to modify constant objects (those returned by +literal expressions), implementations are permitted, though not +required, to share structure between constants where appropriate. Thus +the value of eqv? on constants is sometimes +implementation-dependent.

+

+(eqv? '(a) '(a))                         ===>  unspecified
+(eqv? "a" "a")                           ===>  unspecified
+(eqv? '(b) (cdr '(a b)))                 ===>  unspecified
+(let ((x '(a)))
+  (eqv? x x))                            ===>  #t

+ +

+

+
Rationale:   +The above definition of eqv? allows implementations latitude in +their treatment of procedures and literals: implementations are free +either to detect or to fail to detect that two procedures or two literals +are equivalent to each other, and can decide whether or not to +merge representations of equivalent objects by using the same pointer or +bit pattern to represent both. +
+

+

+ +

+

+

+

+
procedure:  (eq? obj1 obj2) 
+ +

+Eq? is similar to eqv? except that in some cases it is +capable of discerning distinctions finer than those detectable by +eqv?.

+

+Eq? and eqv? are guaranteed to have the same +behavior on symbols, booleans, the empty list, pairs, procedures, +and non-empty +strings and vectors. Eq?'s behavior on numbers and characters is +implementation-dependent, but it will always return either true or +false, and will return true only when eqv? would also return +true. Eq? may also behave differently from eqv? on empty +vectors and empty strings.

+

+(eq? 'a 'a)                             ===>  #t
+(eq? '(a) '(a))                         ===>  unspecified
+(eq? (list 'a) (list 'a))               ===>  #f
+(eq? "a" "a")                           ===>  unspecified
+(eq? "" "")                             ===>  unspecified
+(eq? '() '())                           ===>  #t
+(eq? 2 2)                               ===>  unspecified
+(eq? #\A #\A)         ===>  unspecified
+(eq? car car)                           ===>  #t
+(let ((n (+ 2 3)))
+  (eq? n n))              ===>  unspecified
+(let ((x '(a)))
+  (eq? x x))              ===>  #t
+(let ((x '#()))
+  (eq? x x))              ===>  #t
+(let ((p (lambda (x) x)))
+  (eq? p p))              ===>  #t

+ +

+

+

+

+
Rationale:   It will usually be possible to implement eq? much +more efficiently than eqv?, for example, as a simple pointer +comparison instead of as some more complicated operation. One reason is +that it may not be possible to compute eqv? of two numbers in +constant time, whereas eq? implemented as pointer comparison will +always finish in constant time. Eq? may be used like eqv? +in applications using procedures to implement objects with state since +it obeys the same constraints as eqv?. +
+

+

+ +

+

+

+

+
library procedure:  (equal? obj1 obj2) 
+ +

+Equal? recursively compares the contents of pairs, vectors, and +strings, applying eqv? on other objects such as numbers and symbols. +A rule of thumb is that objects are generally equal? if they print +the same. Equal? may fail to terminate if its arguments are +circular data structures.

+

+(equal? 'a 'a)                          ===>  #t
+(equal? '(a) '(a))                      ===>  #t
+(equal? '(a (b) c)
+        '(a (b) c))                     ===>  #t
+(equal? "abc" "abc")                    ===>  #t
+(equal? 2 2)                            ===>  #t
+(equal? (make-vector 5 'a)
+        (make-vector 5 'a))             ===>  #t
+(equal? (lambda (x) x)
+        (lambda (y) y))          ===>  unspecified

+ +

+

+ +

+

+

+ +

6.2  Numbers

+

+

+

+ + +

+

+Numerical computation has traditionally been neglected by the Lisp +community. Until Common Lisp there was no carefully thought out +strategy for organizing numerical computation, and with the exception of +the MacLisp system [20] little effort was made to +execute numerical code efficiently. This report recognizes the excellent work +of the Common Lisp committee and accepts many of their recommendations. +In some ways this report simplifies and generalizes their proposals in a manner +consistent with the purposes of Scheme.

+

+It is important to distinguish between the mathematical numbers, the +Scheme numbers that attempt to model them, the machine representations +used to implement the Scheme numbers, and notations used to write numbers. +This report uses the types number, complex, real, +rational, and integer to refer to both mathematical numbers +and Scheme numbers. Machine representations such as fixed point and +floating point are referred to by names such as fixnum and +flonum.

+

+ +

+ +

6.2.1  Numerical types

+

+

+

+ + +Mathematically, numbers may be arranged into a tower of subtypes +in which each level is a subset of the level above it: +

+

+ number
+    complex
+    real
+    rational
+    integer +

+ +

+

+For example, 3 is an integer. Therefore 3 is also a rational, +a real, and a complex. The same is true of the Scheme numbers +that model 3. For Scheme numbers, these types are defined by the +predicates number?, complex?, real?, rational?, +and integer?.

+

+There is no simple relationship between a number's type and its +representation inside a computer. Although most implementations of +Scheme will offer at least two different representations of 3, these +different representations denote the same integer.

+

+ +Scheme's numerical operations treat numbers as abstract data, as +independent of their representation as possible. Although an implementation +of Scheme may use fixnum, flonum, and perhaps other representations for +numbers, this should not be apparent to a casual programmer writing +simple programs.

+

+It is necessary, however, to distinguish between numbers that are +represented exactly and those that may not be. For example, indexes +into data structures must be known exactly, as must some polynomial +coefficients in a symbolic algebra system. On the other hand, the +results of measurements are inherently inexact, and irrational numbers +may be approximated by rational and therefore inexact approximations. +In order to catch uses of inexact numbers where exact numbers are +required, Scheme explicitly distinguishes exact from inexact numbers. +This distinction is orthogonal to the dimension of type.

+

+

+ +

6.2.2  Exactness

+

+ +Scheme numbers are either exact or inexact. A number is +exact if it was written as an exact constant or was derived from +exact numbers using only exact operations. A number is +inexact if it was written as an inexact constant, +if it was +derived using inexact ingredients, or if it was derived using +inexact operations. Thus inexactness is a contagious +property of a number. + +If two implementations produce exact results for a +computation that did not involve inexact intermediate results, +the two ultimate results will be mathematically equivalent. This is +generally not true of computations involving inexact numbers +since approximate methods such as floating point arithmetic may be used, +but it is the duty of each implementation to make the result as close as +practical to the mathematically ideal result.

+

+Rational operations such as + should always produce +exact results when given exact arguments. +If the operation is unable to produce an exact result, +then it may either report the violation of an implementation restriction +or it may silently coerce its +result to an inexact value. +See section 6.2.3.

+

+With the exception of inexact->exact, the operations described in +this section must generally return inexact results when given any inexact +arguments. An operation may, however, return an exact result if it can +prove that the value of the result is unaffected by the inexactness of its +arguments. For example, multiplication of any number by an exact zero +may produce an exact zero result, even if the other argument is +inexact.

+

+

+ +

6.2.3  Implementation restrictions

+

+

+Implementations of Scheme are not required to implement the whole +tower of subtypes given in section 6.2.1, +but they must implement a coherent subset consistent with both the +purposes of the implementation and the spirit of the Scheme language. +For example, an implementation in which all numbers are real +may still be quite useful.

+

+Implementations may also support only a limited range of numbers of +any type, subject to the requirements of this section. The supported +range for exact numbers of any type may be different from the +supported range for inexact numbers of that type. For example, +an implementation that uses flonums to represent all its +inexact real numbers may +support a practically unbounded range of exact integers +and rationals +while limiting the range of inexact reals (and therefore +the range of inexact integers and rationals) +to the dynamic range of the flonum format. +Furthermore +the gaps between the representable inexact integers and +rationals are +likely to be very large in such an implementation as the limits of this +range are approached.

+

+An implementation of Scheme must support exact integers +throughout the range of numbers that may be used for indexes of +lists, vectors, and strings or that may result from computing the length of a +list, vector, or string. The length, vector-length, +and string-length procedures must return an exact +integer, and it is an error to use anything but an exact integer as an +index. Furthermore any integer constant within the index range, if +expressed by an exact integer syntax, will indeed be read as an exact +integer, regardless of any implementation restrictions that may apply +outside this range. Finally, the procedures listed below will always +return an exact integer result provided all their arguments are exact integers +and the mathematically expected result is representable as an exact integer +within the implementation:

+

++            -             *
+quotient     remainder     modulo
+max          min           abs
+numerator    denominator   gcd
+lcm          floor         ceiling
+truncate     round         rationalize
+expt

+ +

+Implementations are encouraged, but not required, to support +exact integers and exact rationals of +practically unlimited size and precision, and to implement the +above procedures and the / procedure in +such a way that they always return exact results when given exact +arguments. If one of these procedures is unable to deliver an exact +result when given exact arguments, then it may either report a +violation of an +implementation restriction or it may silently coerce its result to an +inexact number. Such a coercion may cause an error later.

+

+ +An implementation may use floating point and other approximate +representation strategies for inexact numbers. +This report recommends, but does not require, that the IEEE 32-bit +and 64-bit floating point standards be followed by implementations that use +flonum representations, and that implementations using +other representations should match or exceed the precision achievable +using these floating point standards [12].

+

+In particular, implementations that use flonum representations +must follow these rules: A flonum result +must be represented with at least as much precision as is used to express any of +the inexact arguments to that operation. It is desirable (but not required) for +potentially inexact operations such as sqrt, when applied to exact +arguments, to produce exact answers whenever possible (for example the +square root of an exact 4 ought to be an exact 2). +If, however, an +exact number is operated upon so as to produce an inexact result +(as by sqrt), and if the result is represented as a flonum, then +the most precise flonum format available must be used; but if the result +is represented in some other way then the representation must have at least as +much precision as the most precise flonum format available.

+

+Although Scheme allows a variety of written +notations for +numbers, any particular implementation may support only some of them. +For example, an implementation in which all numbers are real +need not support the rectangular and polar notations for complex +numbers. If an implementation encounters an exact numerical constant that +it cannot represent as an exact number, then it may either report a +violation of an implementation restriction or it may silently represent the +constant by an inexact number.

+

+

+ +

6.2.4  Syntax of numerical constants

+

+

+ + +The syntax of the written representations for numbers is described formally in +section 7.1.1. Note that case is not significant in numerical +constants.

+

+ +A number may be written in binary, octal, decimal, or +hexadecimal by the use of a radix prefix. The radix prefixes are #b (binary), #o (octal), #d (decimal), and #x (hexadecimal). With +no radix prefix, a number is assumed to be expressed in decimal.

+

+A +numerical constant may be specified to be either exact or +inexact by a prefix. The prefixes are #e +for exact, and #i for inexact. An exactness +prefix may appear before or after any radix prefix that is used. If +the written representation of a number has no exactness prefix, the +constant may be either inexact or exact. It is +inexact if it contains a decimal point, an +exponent, or a ``#'' character in the place of a digit, +otherwise it is exact. + +In systems with inexact numbers +of varying precisions it may be useful to specify +the precision of a constant. For this purpose, numerical constants +may be written with an exponent marker that indicates the +desired precision of the inexact +representation. The letters s, f, +d, and l specify the use of short, single, +double, and long precision, respectively. (When fewer +than four internal +inexact +representations exist, the four size +specifications are mapped onto those available. For example, an +implementation with two internal representations may map short and +single together and long and double together.) In addition, the +exponent marker e specifies the default precision for the +implementation. The default precision has at least as much precision +as double, but +implementations may wish to allow this default to be set by the user.

+

+3.14159265358979F0
+        Round to single --- 3.141593
+0.6L0
+        Extend to long --- .600000000000000

+ +

+

+ +

6.2.5  Numerical operations

+

The reader is referred to section 1.3.3 for a summary +of the naming conventions used to specify restrictions on the types of +arguments to numerical routines. +The examples used in this section assume that any numerical constant written +using an exact notation is indeed represented as an exact +number. Some examples also assume that certain numerical constants written +using an inexact notation can be represented without loss of +accuracy; the inexact constants were chosen so that this is +likely to be true in implementations that use flonums to represent +inexact numbers.

+

+

+

+

+

+
procedure:  (number? obj) 
+ +
procedure:  (complex? obj) 
+ +
procedure:  (real? obj) 
+ +
procedure:  (rational? obj) 
+ +
procedure:  (integer? obj) 
+ +

+These numerical type predicates can be applied to any kind of +argument, including non-numbers. They return #t if the object is +of the named type, and otherwise they return #f. +In general, if a type predicate is true of a number then all higher +type predicates are also true of that number. Consequently, if a type +predicate is false of a number, then all lower type predicates are +also false of that number. + +If z is an inexact complex number, then (real? z) is true if +and only if (zero? (imag-part z)) is true. If x is an inexact +real number, then (integer? x) is true if and only if +(= x (round x)).

+

+(complex? 3+4i)                 ===>  #t
+(complex? 3)                    ===>  #t
+(real? 3)                       ===>  #t
+(real? -2.5+0.0i)               ===>  #t
+(real? #e1e10)                  ===>  #t
+(rational? 6/10)                ===>  #t
+(rational? 6/3)                 ===>  #t
+(integer? 3+0i)                 ===>  #t
+(integer? 3.0)                  ===>  #t
+(integer? 8/4)                  ===>  #t

+ +

+

+
Note:   +The behavior of these type predicates on inexact numbers +is unreliable, since any inaccuracy may affect the result. +
+

+

+
Note:   +In many implementations the rational? procedure will be the same +as real?, and the complex? procedure will be the same as +number?, but unusual implementations may be able to represent +some irrational numbers exactly or may extend the number system to +support some kind of non-complex numbers. +
+

+

+ +

+

+

+

+
procedure:  (exact? z) 
+ +
procedure:  (inexact? z) 
+ +

+These numerical predicates provide tests for the exactness of a +quantity. For any Scheme number, precisely one of these predicates +is true.

+

+

+ +

+

+

+

+
procedure:  (= z1 z2 z3 ...) 
+ +
procedure:  (< x1 x2 x3 ...) 
+ +
procedure:  (> x1 x2 x3 ...) 
+ +
procedure:  (<= x1 x2 x3 ...) 
+ +
procedure:  (>= x1 x2 x3 ...) 
+ +

+These procedures return #t if their arguments are (respectively): +equal, monotonically increasing, monotonically decreasing, +monotonically nondecreasing, or monotonically nonincreasing.

+

+These predicates are required to be transitive.

+

+

+
Note:   +The traditional implementations of these predicates in Lisp-like +languages are not transitive. +
+

+

+
Note:   +While it is not an error to compare inexact numbers using these +predicates, the results may be unreliable because a small inaccuracy +may affect the result; this is especially true of = and zero?. +When in doubt, consult a numerical analyst. +
+

+

+ +

+

+

+

+
library procedure:  (zero? z) 
+ +
library procedure:  (positive? x) 
+ +
library procedure:  (negative? x) 
+ +
library procedure:  (odd? n) 
+ +
library procedure:  (even? n) 
+ +

+These numerical predicates test a number for a particular property, +returning #t or #f. See note above.

+

+

+ +

+

+

+

+
library procedure:  (max x1 x2 ...) 
+ +
library procedure:  (min x1 x2 ...) 
+ +

+These procedures return the maximum or minimum of their arguments.

+

+(max 3 4)                      ===>  4    ; exact
+(max 3.9 4)                    ===>  4.0  ; inexact

+ +

+

+
Note:   +If any argument is inexact, then the result will also be inexact (unless +the procedure can prove that the inaccuracy is not large enough to affect the +result, which is possible only in unusual implementations). If min or +max is used to compare numbers of mixed exactness, and the numerical +value of the result cannot be represented as an inexact number without loss of +accuracy, then the procedure may report a violation of an implementation +restriction. +
+

+

+ +

+

+

+

+
procedure:  (+ z1 ...) 
+ +
procedure:  (* z1 ...) 
+ +

+These procedures return the sum or product of their arguments. + +(+ 3 4)                         ===>  7
+(+ 3)                           ===>  3
+(+)                             ===>  0
+(* 4)                           ===>  4
+(*)                             ===>  1

+ +

+

+ +

+

+

+

+
procedure:  (- z1 z2) 
+ +
procedure:  (- z) 
+ +
optional procedure:  (- z1 z2 ...) 
+ +
procedure:  (/ z1 z2) 
+ +
procedure:  (/ z) 
+ +
optional procedure:  (/ z1 z2 ...) 
+ +

+With two or more arguments, these procedures return the difference or +quotient of their arguments, associating to the left. With one argument, +however, they return the additive or multiplicative inverse of their argument. + +(- 3 4)                         ===>  -1
+(- 3 4 5)                       ===>  -6
+(- 3)                           ===>  -3
+(/ 3 4 5)                       ===>  3/20
+(/ 3)                           ===>  1/3

+ +

+

+ +

+

+

+

+
library procedure:  (abs x) 
+ +

+Abs returns the absolute value of its argument. +(abs -7)                        ===>  7
+

+ + +

+

+

+

+
procedure:  (quotient n1 n2) 
+ +
procedure:  (remainder n1 n2) 
+ +
procedure:  (modulo n1 n2) 
+ +

+These procedures implement number-theoretic (integer) +division. n2 should be non-zero. All three procedures +return integers. If n1/n2 is an integer: +    (quotient n1 n2)           ===> n1/n2
+    (remainder n1 n2)          ===> 0
+    (modulo n1 n2)             ===> 0
+

+ +If n1/n2 is not an integer: +    (quotient n1 n2)           ===> nq
+    (remainder n1 n2)          ===> nr
+    (modulo n1 n2)             ===> nm
+
+where nq is n1/n2 rounded towards zero, +0 < |nr| < |n2|, 0 < |nm| < |n2|, +nr and nm differ from n1 by a multiple of n2, +nr has the same sign as n1, and +nm has the same sign as n2. +

+From this we can conclude that for integers n1 and n2 with +n2 not equal to 0, +     (= n1 (+ (* n2 (quotient n1 n2))
+           (remainder n1 n2)))
+                                         ===>  #t

+ +provided all numbers involved in that computation are exact. +

+(modulo 13 4)                   ===>  1
+(remainder 13 4)                ===>  1
+
+(modulo -13 4)                  ===>  3
+(remainder -13 4)               ===>  -1
+
+(modulo 13 -4)                  ===>  -3
+(remainder 13 -4)               ===>  1
+
+(modulo -13 -4)                 ===>  -1
+(remainder -13 -4)              ===>  -1
+
+(remainder -13 -4.0)            ===>  -1.0  ; inexact

+ + +

+

+

+

+
library procedure:  (gcd n1 ...) 
+ +
library procedure:  (lcm n1 ...) 
+ +

+These procedures return the greatest common divisor or least common +multiple of their arguments. The result is always non-negative. + +(gcd 32 -36)                    ===>  4
+(gcd)                           ===>  0
+(lcm 32 -36)                    ===>  288
+(lcm 32.0 -36)                  ===>  288.0  ; inexact
+(lcm)                           ===>  1

+ +

+

+ +

+

+

+

+
procedure:  (numerator q) 
+ +
procedure:  (denominator q) 
+ +

+These procedures return the numerator or denominator of their +argument; the result is computed as if the argument was represented as +a fraction in lowest terms. The denominator is always positive. The +denominator of 0 is defined to be 1. + +(numerator (/ 6 4))          ===>  3
+(denominator (/ 6 4))          ===>  2
+(denominator
+  (exact->inexact (/ 6 4)))         ===> 2.0

+ +

+

+ +

+

+

+

+
procedure:  (floor x) 
+ +
procedure:  (ceiling x) 
+ +
procedure:  (truncate x) 
+ +
procedure:  (round x) 
+ +

+These procedures return integers. +Floor returns the largest integer not larger than x. +Ceiling returns the smallest integer not smaller than x. +Truncate returns the integer closest to x whose absolute +value is not larger than the absolute value of x. Round returns the +closest integer to x, rounding to even when x is halfway between two +integers.

+

+

+
Rationale:   +Round rounds to even for consistency with the default rounding +mode specified by the IEEE floating point standard. +
+

+

+
Note:   +If the argument to one of these procedures is inexact, then the result +will also be inexact. If an exact value is needed, the +result should be passed to the inexact->exact procedure. +
+

+(floor -4.3)                  ===>  -5.0
+(ceiling -4.3)                ===>  -4.0
+(truncate -4.3)               ===>  -4.0
+(round -4.3)                  ===>  -4.0
+
+(floor 3.5)                   ===>  3.0
+(ceiling 3.5)                 ===>  4.0
+(truncate 3.5)                ===>  3.0
+(round 3.5)                   ===>  4.0  ; inexact
+
+(round 7/2)                   ===>  4    ; exact
+(round 7)                     ===>  7

+ +

+

+ +

+

+

+

+
library procedure:  (rationalize x y) 
+ + +

+Rationalize returns the simplest rational number +differing from x by no more than y. A rational number r1 is +simpler than another rational number +r2 if r1 = p1/q1 and r2 = p2/q2 (in lowest terms) and |p1| +< |p2| and |q1| < |q2|. Thus 3/5 is simpler than 4/7. +Although not all rationals are comparable in this ordering (consider 2/7 +and 3/5) any interval contains a rational number that is simpler than +every other rational number in that interval (the simpler 2/5 lies +between 2/7 and 3/5). Note that 0 = 0/1 is the simplest rational of +all.

+

+(rationalize
+  (inexact->exact .3) 1/10)          ===> 1/3    ; exact
+(rationalize .3 1/10)                ===> #i1/3  ; inexact

+ +

+

+ +

+

+

+

+
procedure:  (exp z) 
+ +
procedure:  (log z) 
+ +
procedure:  (sin z) 
+ +
procedure:  (cos z) 
+ +
procedure:  (tan z) 
+ +
procedure:  (asin z) 
+ +
procedure:  (acos z) 
+ +
procedure:  (atan z) 
+ +
procedure:  (atan y x) 
+ +

+These procedures are part of every implementation that supports +general +real numbers; they compute the usual transcendental functions. Log +computes the natural logarithm of z (not the base ten logarithm). +Asin, acos, and atan compute arcsine (sin-1), +arccosine (cos-1), and arctangent (tan-1), respectively. +The two-argument variant of atan computes (angle +(make-rectangular x y)) (see below), even in implementations +that don't support general complex numbers.

+

+In general, the mathematical functions log, arcsine, arccosine, and +arctangent are multiply defined. +The value of log z is defined to be the one whose imaginary +part lies in the range from - (exclusive) to (inclusive). +log 0 is undefined. +With log defined this way, the values of sin-1 z, cos-1 z, +and tan-1 z are according to the following formulæ: +

+
sin-1 z = - i log (i z + (1 - z2)1/2)
+

+

+
cos-1 z = / 2 - sin-1 z
+

+

+
tan-1 z = (log (1 + i z) - log (1 - i z)) / (2 i)
+

+

+The above specification follows [27], which in turn +cites [19]; refer to these sources for more detailed +discussion of branch cuts, boundary conditions, and implementation of +these functions. When it is possible these procedures produce a real +result from a real argument.

+

+

+

+

+ +

+

+

+

+
procedure:  (sqrt z) 
+ +

+Returns the principal square root of z. The result will have +either positive real part, or zero real part and non-negative imaginary +part. +

+ +

+

+

+

+
procedure:  (expt z1 z2) 
+ +

+Returns z1 raised to the power z2. For z1 0 +

+
z1z2 = ez2 log z1
+

+0z is 1 if z = 0 and 0 otherwise. +

+ +

+

+

+

+

+

+
procedure:  (make-rectangular x1 x2) 
+ +
procedure:  (make-polar x3 x4) 
+ +
procedure:  (real-part z) 
+ +
procedure:  (imag-part z) 
+ +
procedure:  (magnitude z) 
+ +
procedure:  (angle z) 
+ +

+These procedures are part of every implementation that supports +general +complex numbers. Suppose x1, x2, x3, and x4 are +real numbers and z is a complex number such that +

+
z = x1 + x2i + = x3 · ei x4
+

+Then +(make-rectangular x1 x2)         ===> z
+(make-polar x3 x4)             ===> z
+(real-part z)                          ===> x1
+(imag-part z)                          ===> x2
+(magnitude z)                          ===> |x3|
+(angle z)                              ===> xangle
+

+ +where - < xangle < with xangle = x4 + 2 n +for some integer n. +

+

+
Rationale:   +Magnitude is the same as abs for a real argument, +but abs must be present in all implementations, whereas +magnitude need only be present in implementations that support +general complex numbers. +
+

+

+ +

+

+

+

+
procedure:  (exact->inexact z) 
+ +
procedure:  (inexact->exact z) 
+ +

+Exact->inexact returns an inexact representation of z. +The value returned is the +inexact number that is numerically closest to the argument. +If an exact argument has no reasonably close inexact equivalent, +then a violation of an implementation restriction may be reported.

+

+Inexact->exact returns an exact representation of +z. The value returned is the exact number that is numerically +closest to the argument. +If an inexact argument has no reasonably close exact equivalent, +then a violation of an implementation restriction may be reported.

+

+ +These procedures implement the natural one-to-one correspondence between +exact and inexact integers throughout an +implementation-dependent range. See section 6.2.3.

+

+

+ +

+

+

+

+

+

+

+ +

6.2.6  Numerical input and output

+

+

+
procedure:  (number->string z) 
+ +
procedure:  (number->string z radix) 
+ +

+Radix must be an exact integer, either 2, 8, 10, or 16. If omitted, +radix defaults to 10. +The procedure number->string takes a +number and a radix and returns as a string an external representation of +the given number in the given radix such that +(let ((number number)
+      (radix radix))
+  (eqv? number
+        (string->number (number->string number
+                                        radix)
+                        radix)))
+

+ +is true. It is an error if no possible result makes this expression true. +

+If z is inexact, the radix is 10, and the above expression +can be satisfied by a result that contains a decimal point, +then the result contains a decimal point and is expressed using the +minimum number of digits (exclusive of exponent and trailing +zeroes) needed to make the above expression +true [35]; +otherwise the format of the result is unspecified.

+

+The result returned by number->string +never contains an explicit radix prefix.

+

+

+
Note:   +The error case can occur only when z is not a complex number +or is a complex number with a non-rational real or imaginary part. +
+

+

+
Rationale:   +If z is an inexact number represented using flonums, and +the radix is 10, then the above expression is normally satisfied by +a result containing a decimal point. The unspecified case +allows for infinities, NaNs, and non-flonum representations. +
+

+

+ +

+

+

+

+
procedure:  (string->number string) 
+ +
procedure:  (string->number string radix) 
+ +

+ +Returns a number of the maximally precise representation expressed by the +given string. Radix must be an exact integer, either 2, 8, 10, +or 16. If supplied, radix is a default radix that may be overridden +by an explicit radix prefix in string (e.g. "#o177"). If radix +is not supplied, then the default radix is 10. If string is not +a syntactically valid notation for a number, then string->number +returns #f.

+

+(string->number "100")                ===>  100
+(string->number "100" 16)             ===>  256
+(string->number "1e2")                ===>  100.0
+(string->number "15##")               ===>  1500.0

+ +

+

+
Note:   +The domain of string->number may be restricted by implementations +in the following ways. String->number is permitted to return +#f whenever string contains an explicit radix prefix. +If all numbers supported by an implementation are real, then +string->number is permitted to return #f whenever +string uses the polar or rectangular notations for complex +numbers. If all numbers are integers, then +string->number may return #f whenever +the fractional notation is used. If all numbers are exact, then +string->number may return #f whenever +an exponent marker or explicit exactness prefix is used, or if +a # appears in place of a digit. If all inexact +numbers are integers, then +string->number may return #f whenever +a decimal point is used. +
+

+

+ +

+

+

+ +

6.3  Other data types

+

This section describes operations on some of Scheme's non-numeric data types: +booleans, pairs, lists, symbols, characters, strings and vectors.

+

+

+ +

6.3.1  Booleans

+

+

+The standard boolean objects for true and false are written as +#t and #f. What really +matters, though, are the objects that the Scheme conditional expressions +(if, cond, and, or, do) treat as +true or false. The phrase ``a true value'' +(or sometimes just ``true'') means any object treated as true by the +conditional expressions, and the phrase ``a false value'' (or +``false'') means any object treated as false by the conditional expressions.

+

+Of all the standard Scheme values, only #f +counts as false in conditional expressions. +Except for #f, +all standard Scheme values, including #t, +pairs, the empty list, symbols, numbers, strings, vectors, and procedures, +count as true.

+

+ + +

+
Note:   +Programmers accustomed to other dialects of Lisp should be aware that +Scheme distinguishes both #f and the empty list +from the symbol nil. +
+

+Boolean constants evaluate to themselves, so they do not need to be quoted +in programs.

+

+#t                 ===>  #t
+#f                ===>  #f
+'#f               ===>  #f

+ +

+

+

+
library procedure:  (not obj) 
+ +

+Not returns #t if obj is false, and returns +#f otherwise.

+

+(not #t)           ===>  #f
+(not 3)                  ===>  #f
+(not (list 3))           ===>  #f
+(not #f)          ===>  #t
+(not '())                ===>  #f
+(not (list))             ===>  #f
+(not 'nil)               ===>  #f

+ +

+

+ +

+

+

+

+
library procedure:  (boolean? obj) 
+ +

+Boolean? returns #t if obj is either #t or +#f and returns #f otherwise.

+

+(boolean? #f)          ===>  #t
+(boolean? 0)                  ===>  #f
+(boolean? '())                ===>  #f

+ +

+

+ +

+

+

+ +

6.3.2  Pairs and lists

+

+

+A pair (sometimes called a dotted pair) is a +record structure with two fields called the car and cdr fields (for +historical reasons). Pairs are created by the procedure cons. +The car and cdr fields are accessed by the procedures car and +cdr. The car and cdr fields are assigned by the procedures +set-car! and set-cdr!.

+

+Pairs are used primarily to represent lists. A list can +be defined recursively as either the empty list or a pair whose +cdr is a list. More precisely, the set of lists is defined as the smallest +set X such that

+

+

+ +

+

+The objects in the car fields of successive pairs of a list are the +elements of the list. For example, a two-element list is a pair whose car +is the first element and whose cdr is a pair whose car is the second element +and whose cdr is the empty list. The length of a list is the number of +elements, which is the same as the number of pairs.

+

+The empty list is a special object of its own type +(it is not a pair); it has no elements and its length is zero.

+

+

+
Note:   +The above definitions imply that all lists have finite length and are +terminated by the empty list. +
+

+The most general notation (external representation) for Scheme pairs is +the ``dotted'' notation (c1 . c2) where +c1 is the value of the car field and c2 is the value of the +cdr field. For example (4 . 5) is a pair whose car is 4 and whose +cdr is 5. Note that (4 . 5) is the external representation of a +pair, not an expression that evaluates to a pair.

+

+A more streamlined notation can be used for lists: the elements of the +list are simply enclosed in parentheses and separated by spaces. The +empty list is written () . For example,

+

+(a b c d e)

+ +

+and

+

+(a . (b . (c . (d . (e . ())))))

+ +

+are equivalent notations for a list of symbols.

+

+A chain of pairs not ending in the empty list is called an +improper list. Note that an improper list is not a list. +The list and dotted notations can be combined to represent +improper lists:

+

+(a b c . d)

+ +

+is equivalent to

+

+(a . (b . (c . d)))

+ +

+Whether a given pair is a list depends upon what is stored in the cdr +field. When the set-cdr! procedure is used, an object can be a +list one moment and not the next:

+

+(define x (list 'a 'b 'c))
+(define y x)
+y                               ===>  (a b c)
+(list? y)                       ===>  #t
+(set-cdr! x 4)                  ===>  unspecified
+x                               ===>  (a . 4)
+(eqv? x y)                      ===>  #t
+y                               ===>  (a . 4)
+(list? y)                       ===>  #f
+(set-cdr! x x)                  ===>  unspecified
+(list? x)                       ===>  #f

+ +

+ +Within literal expressions and representations of objects read by the +read procedure, the forms '<datum>, +`<datum>, ,<datum>, and +,@<datum> denote two-element lists whose first elements are +the symbols quote, quasiquote, unquote, and +unquote-splicing, respectively. The second element in each case +is <datum>. This convention is supported so that arbitrary Scheme +programs may be represented as lists. That is, according to Scheme's grammar, every +<expression> is also a <datum> (see section 7.1.2). +Among other things, this permits the use of the read procedure to +parse Scheme programs. See section 3.3.

+

+

+

+
procedure:  (pair? obj) 
+ +

+Pair? returns #t if obj is a pair, and otherwise +returns #f.

+

+(pair? '(a . b))                ===>  #t
+(pair? '(a b c))                ===>  #t
+(pair? '())                     ===>  #f
+(pair? '#(a b))                 ===>  #f

+ + +

+

+

+

+
procedure:  (cons obj1 obj2) 
+ +

+Returns a newly allocated pair whose car is obj1 and whose cdr is +obj2. The pair is guaranteed to be different (in the sense of +eqv?) from every existing object.

+

+(cons 'a '())                   ===>  (a)
+(cons '(a) '(b c d))            ===>  ((a) b c d)
+(cons "a" '(b c))               ===>  ("a" b c)
+(cons 'a 3)                     ===>  (a . 3)
+(cons '(a b) 'c)                ===>  ((a b) . c)

+ + +

+

+

+

+
procedure:  (car pair) 
+ +

+ +Returns the contents of the car field of pair. Note that it is an +error to take the car of the empty list.

+

+(car '(a b c))                  ===>  a
+(car '((a) b c d))              ===>  (a)
+(car '(1 . 2))                  ===>  1
+(car '())                       ===>  error

+ +

+

+ +

+

+

+

+
procedure:  (cdr pair) 
+ +

+ +Returns the contents of the cdr field of pair. +Note that it is an error to take the cdr of the empty list.

+

+(cdr '((a) b c d))              ===>  (b c d)
+(cdr '(1 . 2))                  ===>  2
+(cdr '())                       ===>  error

+ +

+

+ +

+

+

+

+
procedure:  (set-car! pair obj) 
+ +

+ +Stores obj in the car field of pair. +The value returned by set-car! is unspecified. +(define (f) (list 'not-a-constant-list))
+(define (g) '(constant-list))
+(set-car! (f) 3)                     ===>  unspecified
+(set-car! (g) 3)                     ===>  error

+ +

+

+ +

+

+

+

+
procedure:  (set-cdr! pair obj) 
+ +

+ +Stores obj in the cdr field of pair. +The value returned by set-cdr! is unspecified. +

+ +

+

+

+

+

+

+
library procedure:  (caar pair) 
+ +
library procedure:  (cadr pair) 
+ +
                                                :                                                    
+ +
library procedure:  (cdddar pair) 
+ +
library procedure:  (cddddr pair) 
+ +

+These procedures are compositions of car and cdr, where +for example caddr could be defined by

+

+(define caddr (lambda (x) (car (cdr (cdr x))))).

+ +

+Arbitrary compositions, up to four deep, are provided. There are +twenty-eight of these procedures in all.

+

+

+ +

+

+

+

+
library procedure:  (null? obj) 
+ +

+Returns #t if obj is the empty list, +otherwise returns #f.

+

+ +

+ +

+

+

+

+
library procedure:  (list? obj) 
+ +

+Returns #t if obj is a list, otherwise returns #f. +By definition, all lists have finite length and are terminated by +the empty list.

+

+        (list? '(a b c))             ===>  #t
+        (list? '())                  ===>  #t
+        (list? '(a . b))             ===>  #f
+        (let ((x (list 'a)))
+          (set-cdr! x x)
+          (list? x))                 ===>  #f

+ + +

+

+

+

+
library procedure:  (list obj ...) 
+ +

+Returns a newly allocated list of its arguments.

+

+(list 'a (+ 3 4) 'c)                    ===>  (a 7 c)
+(list)                                  ===>  ()

+ + +

+

+

+

+
library procedure:  (length list) 
+ +

+ +Returns the length of list.

+

+(length '(a b c))                       ===>  3
+(length '(a (b) (c d e)))               ===>  3
+(length '())                            ===>  0

+ + +

+

+

+

+
library procedure:  (append list ...) 
+ +

+ +Returns a list consisting of the elements of the first list +followed by the elements of the other lists.

+

+(append '(x) '(y))                      ===>  (x y)
+(append '(a) '(b c d))                  ===>  (a b c d)
+(append '(a (b)) '((c)))                ===>  (a (b) (c))

+ +

+The resulting list is always newly allocated, except that it shares +structure with the last list argument. The last argument may +actually be any object; an improper list results if the last argument is not a +proper list.

+

+(append '(a b) '(c . d))                ===>  (a b c . d)
+(append '() 'a)                         ===>  a

+ + +

+

+

+

+
library procedure:  (reverse list) 
+ +

+ +Returns a newly allocated list consisting of the elements of list +in reverse order.

+

+(reverse '(a b c))                      ===>  (c b a)
+(reverse '(a (b c) d (e (f))))  
                ===>  ((e (f)) d (b c) a)

+ + +

+

+

+

+
library procedure:  (list-tail list k) 
+ +

+Returns the sublist of list obtained by omitting the first k +elements. It is an error if list has fewer than k elements. +List-tail could be defined by

+

+(define list-tail
+  (lambda (x k)
+    (if (zero? k)
+        x
+        (list-tail (cdr x) (- k 1)))))

+ + +

+

+

+

+
library procedure:  (list-ref list k) 
+ +

+Returns the kth element of list. (This is the same +as the car of (list-tail list k).) +It is an error if list has fewer than k elements.

+

+(list-ref '(a b c d) 2)                         ===>  c
+(list-ref '(a b c d)
+          (inexact->exact (round 1.8))) 
                ===>  c

+ + +

+

+

+

+

+

+
library procedure:  (memq obj list) 
+ +
library procedure:  (memv obj list) 
+ +
library procedure:  (member obj list) 
+ +

+These procedures return the first sublist of list whose car is +obj, where the sublists of list are the non-empty lists +returned by (list-tail list k) for k less +than the length of list. If +obj does not occur in list, then #f (not the empty list) is +returned. Memq uses eq? to compare obj with the elements of +list, while memv uses eqv? and member uses equal?.

+

+(memq 'a '(a b c))                      ===>  (a b c)
+(memq 'b '(a b c))                      ===>  (b c)
+(memq 'a '(b c d))                      ===>  #f
+(memq (list 'a) '(b (a) c))             ===>  #f
+(member (list 'a)
+        '(b (a) c))                     ===>  ((a) c)
+(memq 101 '(100 101 102))               ===>  unspecified
+(memv 101 '(100 101 102))               ===>  (101 102)

+ +

+

+ +

+

+

+

+
library procedure:  (assq obj alist) 
+ +
library procedure:  (assv obj alist) 
+ +
library procedure:  (assoc obj alist) 
+ +

+Alist (for ``association list'') must be a list of +pairs. These procedures find the first pair in alist whose car field is obj, +and returns that pair. If no pair in alist has obj as its +car, then #f (not the empty list) is returned. Assq uses +eq? to compare obj with the car fields of the pairs in alist, +while assv uses eqv? and assoc uses equal?.

+

+(define e '((a 1) (b 2) (c 3)))
+(assq 'a e)             ===>  (a 1)
+(assq 'b e)             ===>  (b 2)
+(assq 'd e)             ===>  #f
+(assq (list 'a) '(((a)) ((b)) ((c))))
+                        ===>  #f
+(assoc (list 'a) '(((a)) ((b)) ((c))))   
+                                   ===>  ((a))
+(assq 5 '((2 3) (5 7) (11 13)))    
+                                   ===>  unspecified
+(assv 5 '((2 3) (5 7) (11 13)))    
+                                   ===>  (5 7)

+ +

+

+
Rationale:   +Although they are ordinarily used as predicates, +memq, memv, member, assq, assv, and assoc do not +have question marks in their names because they return useful values rather +than just #t or #f. +
+ +

+

+

+ +

6.3.3  Symbols

+

+

+Symbols are objects whose usefulness rests on the fact that two +symbols are identical (in the sense of eqv?) if and only if their +names are spelled the same way. This is exactly the property needed to +represent identifiers in programs, and so most +implementations of Scheme use them internally for that purpose. Symbols +are useful for many other applications; for instance, they may be used +the way enumerated values are used in Pascal.

+

+The rules for writing a symbol are exactly the same as the rules for +writing an identifier; see sections 2.1 +and 7.1.1.

+

+It is guaranteed that any symbol that has been returned as part of +a literal expression, or read using the read procedure, and +subsequently written out using the write procedure, will read back +in as the identical symbol (in the sense of eqv?). The +string->symbol procedure, however, can create symbols for +which this write/read invariance may not hold because their names +contain special characters or letters in the non-standard case.

+

+

+
Note:   +Some implementations of Scheme have a feature known as ``slashification'' +in order to guarantee write/read invariance for all symbols, but +historically the most important use of this feature has been to +compensate for the lack of a string data type. +

+Some implementations also have ``uninterned symbols'', which +defeat write/read invariance even in implementations with slashification, +and also generate exceptions to the rule that two symbols are the same +if and only if their names are spelled the same. +

+
+

+

+

+
procedure:  (symbol? obj) 
+ +

+Returns #t if obj is a symbol, otherwise returns #f.

+

+(symbol? 'foo)                  ===>  #t
+(symbol? (car '(a b)))          ===>  #t
+(symbol? "bar")                 ===>  #f
+(symbol? 'nil)                  ===>  #t
+(symbol? '())                   ===>  #f
+(symbol? #f)             ===>  #f

+ + +

+

+

+

+
procedure:  (symbol->string symbol) 
+ +

+Returns the name of symbol as a string. If the symbol was part of +an object returned as the value of a literal expression +(section 4.1.2) or by a call to the read procedure, +and its name contains alphabetic characters, then the string returned +will contain characters in the implementation's preferred standard +case -- some implementations will prefer upper case, others lower case. +If the symbol was returned by string->symbol, the case of +characters in the string returned will be the same as the case in the +string that was passed to string->symbol. It is an error +to apply mutation procedures like string-set! to strings returned +by this procedure.

+

+The following examples assume that the implementation's standard case is +lower case:

+

+(symbol->string 'flying-fish)     
+                                          ===>  "flying-fish"
+(symbol->string 'Martin)                  ===>  "martin"
+(symbol->string
+   (string->symbol "Malvina"))     
+                                          ===>  "Malvina"

+ + +

+

+

+

+
procedure:  (string->symbol string) 
+ +

+Returns the symbol whose name is string. This procedure can +create symbols with names containing special characters or letters in +the non-standard case, but it is usually a bad idea to create such +symbols because in some implementations of Scheme they cannot be read as +themselves. See symbol->string.

+

+The following examples assume that the implementation's standard case is +lower case:

+

+(eq? 'mISSISSIppi 'mississippi)  
                ===>  #t
+(string->symbol "mISSISSIppi")  
                ===>  the symbol with name "mISSISSIppi"
+(eq? 'bitBlt (string->symbol "bitBlt"))     
                ===>  #f
+(eq? 'JollyWog
+     (string->symbol
+       (symbol->string 'JollyWog)))  
                ===>  #t
+(string=? "K. Harper, M.D."
+          (symbol->string
+            (string->symbol "K. Harper, M.D.")))  
                ===>  #t

+ +

+

+ +

+

+

+ +

6.3.4  Characters

+

+

+Characters are objects that represent printed characters such as +letters and digits. +Characters are written using the notation #\<character> +or #\<character name>. +For example:

+

+

+
+ + + + + + + + +
#\a; lower case letter
#\A; upper case letter
#\(; left parenthesis
#\ ; the space character
#\space; the preferred way to write a space
#\newline; the newline character
+
+

+

+Case is significant in #\<character>, but not in +#\<character name>. If <character> in +#\<character> is alphabetic, then the character +following <character> must be a delimiter character such as a +space or parenthesis. This rule resolves the ambiguous case where, for +example, the sequence of characters ``#\space'' +could be taken to be either a representation of the space character or a +representation of the character ``#\s'' followed +by a representation of the symbol ``pace.''

+

+ +Characters written in the #\ notation are self-evaluating. +That is, they do not have to be quoted in programs. + +Some of the procedures that operate on characters ignore the +difference between upper case and lower case. The procedures that +ignore case have ``-ci'' (for ``case +insensitive'') embedded in their names.

+

+

+

+
procedure:  (char? obj) 
+ +

+Returns #t if obj is a character, otherwise returns #f.

+

+

+ +

+

+

+

+
procedure:  (char=? char1 char2) 
+ +
procedure:  (char<? char1 char2) 
+ +
procedure:  (char>? char1 char2) 
+ +
procedure:  (char<=? char1 char2) 
+ +
procedure:  (char>=? char1 char2) 
+ +

+ + +These procedures impose a total ordering on the set of characters. It +is guaranteed that under this ordering:

+

+

+ +

+

+Some implementations may generalize these procedures to take more than +two arguments, as with the corresponding numerical predicates.

+

+

+ +

+

+

+

+
library procedure:  (char-ci=? char1 char2) 
+ +
library procedure:  (char-ci<? char1 char2) 
+ +
library procedure:  (char-ci>? char1 char2) 
+ +
library procedure:  (char-ci<=? char1 char2) 
+ +
library procedure:  (char-ci>=? char1 char2) 
+ +

+ +These procedures are similar to char=? et cetera, but they treat +upper case and lower case letters as the same. For example, (char-ci=? #\A #\a) returns #t. Some +implementations may generalize these procedures to take more than two +arguments, as with the corresponding numerical predicates.

+

+

+ +

+

+

+

+
library procedure:  (char-alphabetic? char) 
+ +
library procedure:  (char-numeric? char) 
+ +
library procedure:  (char-whitespace? char) 
+ +
library procedure:  (char-upper-case? letter) 
+ +
library procedure:  (char-lower-case? letter) 
+ +

+These procedures return #t if their arguments are alphabetic, +numeric, whitespace, upper case, or lower case characters, respectively, +otherwise they return #f. The following remarks, which are specific to +the ASCII character set, are intended only as a guide: The alphabetic characters +are the 52 upper and lower case letters. The numeric characters are the +ten decimal digits. The whitespace characters are space, tab, line +feed, form feed, and carriage return. +

+ +

+

+

+

+

+

+
procedure:  (char->integer char) 
+ +
procedure:  (integer->char n) 
+ +

+Given a character, char->integer returns an exact integer +representation of the character. Given an exact integer that is the image of +a character under char->integer, integer->char +returns that character. These procedures implement order-preserving isomorphisms +between the set of characters under the char<=? ordering and some +subset of the integers under the <= ordering. That is, if

+

+(char<=? a b) ===> #t  and  (<= x y) ===> #t

+ +

+and x and y are in the domain of +integer->char, then

+

+(<= (char->integer a)
+    (char->integer b))                 ===>  #t
+
+(char<=? (integer->char x)
+         (integer->char y))             ===>  #t

+ +

+

+ +

+

+

+

+
library procedure:  (char-upcase char) 
+ +
library procedure:  (char-downcase char) 
+ +

+ +These procedures return a character char2 such that (char-ci=? char char2). In addition, if char is +alphabetic, then the result of char-upcase is upper case and the +result of char-downcase is lower case.

+

+

+ +

+

+

+ +

6.3.5  Strings

+

+

+Strings are sequences of characters. +Strings are written as sequences of characters enclosed within doublequotes +("). A doublequote can be written inside a string only by escaping +it with a backslash (\), as in

+

+"The word \"recursion\" has many meanings."

+ +

+A backslash can be written inside a string only by escaping it with another +backslash. Scheme does not specify the effect of a backslash within a +string that is not followed by a doublequote or backslash.

+

+A string constant may continue from one line to the next, but +the exact contents of such a string are unspecified. + +The length of a string is the number of characters that it +contains. This number is an exact, non-negative integer that is fixed when the +string is created. The valid indexes of a string are the +exact non-negative integers less than the length of the string. The first +character of a string has index 0, the second has index 1, and so on.

+

+In phrases such as ``the characters of string beginning with +index start and ending with index end,'' it is understood +that the index start is inclusive and the index end is +exclusive. Thus if start and end are the same index, a null +substring is referred to, and if start is zero and end is +the length of string, then the entire string is referred to.

+

+Some of the procedures that operate on strings ignore the +difference between upper and lower case. The versions that ignore case +have ``-ci'' (for ``case insensitive'') embedded in their +names.

+

+

+

+
procedure:  (string? obj) 
+ +

+Returns #t if obj is a string, otherwise returns #f. +

+ +

+

+

+

+
procedure:  (make-string k) 
+ +
procedure:  (make-string k char) 
+ +

+Make-string returns a newly allocated string of +length k. If char is given, then all elements of the string +are initialized to char, otherwise the contents of the +string are unspecified.

+

+

+ +

+

+

+

+
library procedure:  (string char ...) 
+ +

+Returns a newly allocated string composed of the arguments.

+

+

+ +

+

+

+

+
procedure:  (string-length string) 
+ +

+Returns the number of characters in the given string. +

+ +

+

+

+

+
procedure:  (string-ref string k) 
+ +

+k must be a valid index of string. +String-ref returns character k of string using zero-origin indexing. +

+ +

+

+

+

+
procedure:  (string-set! string k char) 
+ +

+k must be a valid index of string. +String-set! stores char in element k of string +and returns an unspecified value. +(define (f) (make-string 3 #\*))
+(define (g) "***")
+(string-set! (f) 0 #\?)          ===>  unspecified
+(string-set! (g) 0 #\?)          ===>  error
+(string-set! (symbol->string 'immutable)
+             0
+             #\?)          ===>  error

+ +

+

+ +

+

+

+

+
library procedure:  (string=? string1 string2) 
+ +
library procedure:  (string-ci=? string1 string2) 
+ +

+Returns #t if the two strings are the same length and contain the same +characters in the same positions, otherwise returns #f. +String-ci=? treats +upper and lower case letters as though they were the same character, but +string=? treats upper and lower case as distinct characters.

+

+

+ +

+

+

+

+
library procedure:  (string<? string1 string2) 
+ +
library procedure:  (string>? string1 string2) 
+ +
library procedure:  (string<=? string1 string2) 
+ +
library procedure:  (string>=? string1 string2) 
+ +
library procedure:  (string-ci<? string1 string2) 
+ +
library procedure:  (string-ci>? string1 string2) 
+ +
library procedure:  (string-ci<=? string1 string2) 
+ +
library procedure:  (string-ci>=? string1 string2) 
+ +

+These procedures are the lexicographic extensions to strings of the +corresponding orderings on characters. For example, string<? is +the lexicographic ordering on strings induced by the ordering +char<? on characters. If two strings differ in length but +are the same up to the length of the shorter string, the shorter string +is considered to be lexicographically less than the longer string.

+

+Implementations may generalize these and the string=? and +string-ci=? procedures to take more than two arguments, as with +the corresponding numerical predicates.

+

+

+ +

+

+

+

+
library procedure:  (substring string start end) 
+ +

+String must be a string, and start and end +must be exact integers satisfying +

+
0 < start < end < (string-length string).
+

+Substring returns a newly allocated string formed from the characters of +string beginning with index start (inclusive) and ending with index +end (exclusive). +

+ +

+

+

+

+
library procedure:  (string-append string ...) 
+ +

+Returns a newly allocated string whose characters form the concatenation of the +given strings.

+

+

+ +

+

+

+

+
library procedure:  (string->list string) 
+ +
library procedure:  (list->string list) 
+ +

+String->list returns a newly allocated list of the +characters that make up the given string. List->string +returns a newly allocated string formed from the characters in the list +list, which must be a list of characters. String->list +and list->string are +inverses so far as equal? is concerned. + +

+ +

+

+

+

+
library procedure:  (string-copy string) 
+ +

+Returns a newly allocated copy of the given string.

+

+

+ +

+

+

+

+
library procedure:  (string-fill! string char) 
+ +

+Stores char in every element of the given string and returns an +unspecified value. +

+ +

+

+

+ +

6.3.6  Vectors

+

+

+Vectors are heterogenous structures whose elements are indexed +by integers. A vector typically occupies less space than a list +of the same length, and the average time required to access a randomly +chosen element is typically less for the vector than for the list.

+

+The length of a vector is the number of elements that it +contains. This number is a non-negative integer that is fixed when the +vector is created. The valid indexes of a +vector are the exact non-negative integers less than the length of the +vector. The first element in a vector is indexed by zero, and the last +element is indexed by one less than the length of the vector.

+

+Vectors are written using the notation #(obj ...). +For example, a vector of length 3 containing the number zero in element +0, the list (2 2 2 2) in element 1, and the string "Anna" in +element 2 can be written as following:

+

+#(0 (2 2 2 2) "Anna")

+ +

+Note that this is the external representation of a vector, not an +expression evaluating to a vector. Like list constants, vector +constants must be quoted:

+

+'#(0 (2 2 2 2) "Anna")  
                ===>  #(0 (2 2 2 2) "Anna")

+ +

+

+

+

+

+
procedure:  (vector? obj) 
+ +

+Returns #t if obj is a vector, otherwise returns #f. +

+ +

+

+

+

+
procedure:  (make-vector k) 
+ +
procedure:  (make-vector k fill) 
+ +

+Returns a newly allocated vector of k elements. If a second +argument is given, then each element is initialized to fill. +Otherwise the initial contents of each element is unspecified.

+

+

+ +

+

+

+

+
library procedure:  (vector obj ...) 
+ +

+Returns a newly allocated vector whose elements contain the given +arguments. Analogous to list.

+

+(vector 'a 'b 'c)                       ===>  #(a b c)

+ + +

+

+

+

+
procedure:  (vector-length vector) 
+ +

+Returns the number of elements in vector as an exact integer. +

+ +

+

+

+

+
procedure:  (vector-ref vector k) 
+ +

+k must be a valid index of vector. +Vector-ref returns the contents of element k of +vector.

+

+(vector-ref '#(1 1 2 3 5 8 13 21)
+            5)  
                ===>  8
+(vector-ref '#(1 1 2 3 5 8 13 21)
+            (let ((i (round (* 2 (acos -1)))))
+              (if (inexact? i)
+                  (inexact->exact i)
+                  i))) 
                ===> 13

+ + +

+

+

+

+
procedure:  (vector-set! vector k obj) 
+ +

+k must be a valid index of vector. +Vector-set! stores obj in element k of vector. +The value returned by vector-set! is unspecified. +(let ((vec (vector 0 '(2 2 2 2) "Anna")))
+  (vector-set! vec 1 '("Sue" "Sue"))
+  vec)      
                ===>  #(0 ("Sue" "Sue") "Anna")
+
+(vector-set! '#(0 1 2) 1 "doe")  
                ===>  error  ; constant vector

+ + +

+

+

+

+
library procedure:  (vector->list vector) 
+ +
library procedure:  (list->vector list) 
+ +

+Vector->list returns a newly allocated list of the objects contained +in the elements of vector. List->vector returns a newly +created vector initialized to the elements of the list list.

+

+(vector->list '#(dah dah didah))  
                ===>  (dah dah didah)
+(list->vector '(dididit dah))   
                ===>  #(dididit dah)

+ + +

+

+

+

+
library procedure:  (vector-fill! vector fill) 
+ +

+Stores fill in every element of vector. +The value returned by vector-fill! is unspecified. +

+ +

+

+

+ +

6.4  Control features

+

+

+ +This chapter describes various primitive procedures which control the +flow of program execution in special ways. +The procedure? predicate is also described here.

+

+

+

+

+

+
procedure:  (procedure? obj) 
+ +

+Returns #t if obj is a procedure, otherwise returns #f.

+

+(procedure? car)                    ===>  #t
+(procedure? 'car)                   ===>  #f
+(procedure? (lambda (x) (* x x)))   
+                                    ===>  #t
+(procedure? '(lambda (x) (* x x)))  
+                                    ===>  #f
+(call-with-current-continuation procedure?)
+                                    ===>  #t

+ +

+

+ +

+

+

+

+
procedure:  (apply proc arg1 ... args) 
+ +

+Proc must be a procedure and args must be a list. +Calls proc with the elements of the list +(append (list arg1 ...) args) as the actual +arguments.

+

+(apply + (list 3 4))                      ===>  7
+
+(define compose
+  (lambda (f g)
+    (lambda args
+      (f (apply g args)))))
+
+((compose sqrt *) 12 75)                      ===>  30

+ + +

+

+

+

+
library procedure:  (map proc list1 list2 ...) 
+ +

+The lists must be lists, and proc must be a +procedure taking as many arguments as there are lists +and returning a single value. If more +than one list is given, then they must all be the same length. +Map applies proc element-wise to the elements of the +lists and returns a list of the results, in order. +The dynamic order in which proc is applied to the elements of the +lists is unspecified.

+

+(map cadr '((a b) (d e) (g h)))   
                ===>  (b e h)
+
+(map (lambda (n) (expt n n))
+     '(1 2 3 4 5))                
                ===>  (1 4 27 256 3125)
+
+(map + '(1 2 3) '(4 5 6))                 ===>  (5 7 9)
+
+(let ((count 0))
+  (map (lambda (ignored)
+         (set! count (+ count 1))
+         count)
+       '(a b)))                         ===>  (1 2) or (2 1)
+

+ +

+

+ +

+

+

+

+
library procedure:  (for-each proc list1 list2 ...) 
+ +

+The arguments to for-each are like the arguments to map, but +for-each calls proc for its side effects rather than for its +values. Unlike map, for-each is guaranteed to call proc on +the elements of the lists in order from the first element(s) to the +last, and the value returned by for-each is unspecified.

+

+(let ((v (make-vector 5)))
+  (for-each (lambda (i)
+              (vector-set! v i (* i i)))
+            '(0 1 2 3 4))
+  v)                                        ===>  #(0 1 4 9 16)

+ +

+

+ +

+

+

+

+
library procedure:  (force promise) 
+ +

+Forces the value of promise (see delay, +section 4.2.5). If no value has been computed for +the promise, then a value is computed and returned. The value of the +promise is cached (or ``memoized'') so that if it is forced a second +time, the previously computed value is returned. + +(force (delay (+ 1 2)))           ===>  3
+(let ((p (delay (+ 1 2))))
+  (list (force p) (force p)))  
+                                       ===>  (3 3)
+
+(define a-stream
+  (letrec ((next
+            (lambda (n)
+              (cons n (delay (next (+ n 1)))))))
+    (next 0)))
+(define head car)
+(define tail
+  (lambda (stream) (force (cdr stream))))
+
+(head (tail (tail a-stream)))  
+                                       ===>  2

+ +

+Force and delay are mainly intended for programs written in +functional style. The following examples should not be considered to +illustrate good programming style, but they illustrate the property that +only one value is computed for a promise, no matter how many times it is +forced. + +(define count 0)
+(define p
+  (delay (begin (set! count (+ count 1))
+                (if (> count x)
+                    count
+                    (force p)))))
+(define x 5)
+p                             ===>  a promise
+(force p)                     ===>  6
+p                             ===>  a promise, still
+(begin (set! x 10)
+       (force p))             ===>  6

+ +

+Here is a possible implementation of delay and force. +Promises are implemented here as procedures of no arguments, +and force simply calls its argument:

+

+(define force
+  (lambda (object)
+    (object)))

+ +

+We define the expression

+

+(delay <expression>)

+ +

+to have the same meaning as the procedure call

+

+(make-promise (lambda () <expression>))
+

+ +

+as follows

+

+(define-syntax delay
+  (syntax-rules ()
+    ((delay expression)
+     (make-promise (lambda () expression))))),

+ +

+where make-promise is defined as follows:

+

+ +(define make-promise
+  (lambda (proc)
+    (let ((result-ready? #f)
+          (result #f))
+      (lambda ()
+        (if result-ready?
+            result
+            (let ((x (proc)))
+              (if result-ready?
+                  result
+                  (begin (set! result-ready? #t)
+                         (set! result x)
+                         result))))))))

+ +

+

+
Rationale:   +A promise may refer to its own value, as in the last example above. +Forcing such a promise may cause the promise to be forced a second time +before the value of the first force has been computed. +This complicates the definition of make-promise. +
+

+Various extensions to this semantics of delay and force +are supported in some implementations:

+

+

+ +

+

+ +

+

+

+

+
procedure:  (call-with-current-continuation proc) 
+ +

+ Proc must be a procedure of one +argument. The procedure call-with-current-continuation packages +up the current continuation (see the rationale below) as an ``escape +procedure'' and passes it as an argument to +proc. The escape procedure is a Scheme procedure that, if it is +later called, will abandon whatever continuation is in effect at that later +time and will instead use the continuation that was in effect +when the escape procedure was created. Calling the escape procedure +may cause the invocation of before and after thunks installed using +dynamic-wind.

+

+The escape procedure accepts the same number of arguments as the continuation to +the original call to call-with-current-continuation. +Except for continuations created by the call-with-values +procedure, all continuations take exactly one value. The +effect of passing no value or more than one value to continuations +that were not created by call-with-values is unspecified.

+

+The escape procedure that is passed to proc has +unlimited extent just like any other procedure in Scheme. It may be stored +in variables or data structures and may be called as many times as desired.

+

+The following examples show only the most common ways in which +call-with-current-continuation is used. If all real uses were as +simple as these examples, there would be no need for a procedure with +the power of call-with-current-continuation.

+

+(call-with-current-continuation
+  (lambda (exit)
+    (for-each (lambda (x)
+                (if (negative? x)
+                    (exit x)))
+              '(54 0 37 -3 245 19))
+    #t))                                ===>  -3
+
+(define list-length
+  (lambda (obj)
+    (call-with-current-continuation
+      (lambda (return)
+        (letrec ((r
+                  (lambda (obj)
+                    (cond ((null? obj) 0)
+                          ((pair? obj)
+                           (+ (r (cdr obj)) 1))
+                          (else (return #f))))))
+          (r obj))))))
+
+(list-length '(1 2 3 4))                    ===>  4
+
+(list-length '(a b . c))                    ===>  #f

+ +

+

+
Rationale:   +

+A common use of call-with-current-continuation is for +structured, non-local exits from loops or procedure bodies, but in fact +call-with-current-continuation is extremely useful for implementing a +wide variety of advanced control structures.

+

+Whenever a Scheme expression is evaluated there is a +continuation wanting the result of the expression. The continuation +represents an entire (default) future for the computation. If the expression is +evaluated at top level, for example, then the continuation might take the +result, print it on the screen, prompt for the next input, evaluate it, and +so on forever. Most of the time the continuation includes actions +specified by user code, as in a continuation that will take the result, +multiply it by the value stored in a local variable, add seven, and give +the answer to the top level continuation to be printed. Normally these +ubiquitous continuations are hidden behind the scenes and programmers do not +think much about them. On rare occasions, however, a programmer may +need to deal with continuations explicitly. +Call-with-current-continuation allows Scheme programmers to do +that by creating a procedure that acts just like the current +continuation.

+

+Most programming languages incorporate one or more special-purpose +escape constructs with names like exit, return, or +even goto. In 1965, however, Peter Landin [16] +invented a general purpose escape operator called the J-operator. John +Reynolds [24] described a simpler but equally powerful +construct in 1972. The catch special form described by Sussman +and Steele in the 1975 report on Scheme is exactly the same as +Reynolds's construct, though its name came from a less general construct +in MacLisp. Several Scheme implementors noticed that the full power of the +catch construct could be provided by a procedure instead of by a +special syntactic construct, and the name +call-with-current-continuation was coined in 1982. This name is +descriptive, but opinions differ on the merits of such a long name, and +some people use the name call/cc instead. +

+
+

+

+ +

+

+

+

+
procedure:  (values obj ...) 
+ +

+Delivers all of its arguments to its continuation. +Except for continuations created by the call-with-values +procedure, all continuations take exactly one value. +Values might be defined as follows: +(define (values . things)
+  (call-with-current-continuation 
+    (lambda (cont) (apply cont things))))
+

+ +

+

+ +

+

+

+

+
procedure:  (call-with-values producer consumer) 
+ +

+Calls its producer argument with no values and +a continuation that, when passed some values, calls the +consumer procedure with those values as arguments. +The continuation for the call to consumer is the +continuation of the call to call-with-values.

+

+(call-with-values (lambda () (values 4 5))
+                  (lambda (a b) b))
+                                                           ===>  5
+
+(call-with-values * -)                                     ===>  -1
+

+ +

+

+ +

+

+

+

+
procedure:  (dynamic-wind before thunk after) 
+ +

+Calls thunk without arguments, returning the result(s) of this call. +Before and after are called, also without arguments, as required +by the following rules (note that in the absence of calls to continuations +captured using call-with-current-continuation the three arguments are +called once each, in order). Before is called whenever execution +enters the dynamic extent of the call to thunk and after is called +whenever it exits that dynamic extent. The dynamic extent of a procedure +call is the period between when the call is initiated and when it +returns. In Scheme, because of call-with-current-continuation, the +dynamic extent of a call may not be a single, connected time period. +It is defined as follows: +

+ +

+

+If a second call to dynamic-wind occurs within the dynamic extent of the +call to thunk and then a continuation is invoked in such a way that the +afters from these two invocations of dynamic-wind are both to be +called, then the after associated with the second (inner) call to +dynamic-wind is called first.

+

+If a second call to dynamic-wind occurs within the dynamic extent of the +call to thunk and then a continuation is invoked in such a way that the +befores from these two invocations of dynamic-wind are both to be +called, then the before associated with the first (outer) call to +dynamic-wind is called first.

+

+If invoking a continuation requires calling the before from one call +to dynamic-wind and the after from another, then the after +is called first.

+

+The effect of using a captured continuation to enter or exit the dynamic +extent of a call to before or after is undefined.

+

+(let ((path '())
+      (c #f))
+  (let ((add (lambda (s)
+               (set! path (cons s path)))))
+    (dynamic-wind
+      (lambda () (add 'connect))
+      (lambda ()
+        (add (call-with-current-continuation
+               (lambda (c0)
+                 (set! c c0)
+                 'talk1))))
+      (lambda () (add 'disconnect)))
+    (if (< (length path) 4)
+        (c 'talk2)
+        (reverse path))))
+    
                ===> (connect talk1 disconnect
+               connect talk2 disconnect)
+

+ + +

+

+

+ +

6.5  Eval

+

+

+
procedure:  (eval expression environment-specifier) 
+ +

+Evaluates expression in the specified environment and returns its value. +Expression must be a valid Scheme expression represented as data, +and environment-specifier must be a value returned by one of the +three procedures described below. +Implementations may extend eval to allow non-expression programs +(definitions) as the first argument and to allow other +values as environments, with the restriction that eval is not +allowed to create new bindings in the environments associated with +null-environment or scheme-report-environment.

+

+(eval '(* 7 3) (scheme-report-environment 5))
+                                                           ===>  21
+
+(let ((f (eval '(lambda (f x) (f x x))
+               (null-environment 5))))
+  (f + 10))
+                                                           ===>  20
+

+ +

+

+ +

+

+

+

+
procedure:  (scheme-report-environment version) 
+ +
procedure:  (null-environment version) 
+ +

+Version must be the exact integer 5, +corresponding to this revision of the Scheme report (the +Revised5 Report on Scheme). +Scheme-report-environment returns a specifier for an +environment that is empty except for all bindings defined in +this report that are either required or both optional and +supported by the implementation. Null-environment returns +a specifier for an environment that is empty except for the +(syntactic) bindings for all syntactic keywords defined in +this report that are either required or both optional and +supported by the implementation.

+

+Other values of version can be used to specify environments +matching past revisions of this report, but their support is not +required. An implementation will signal an error if version +is neither 5 nor another value supported by +the implementation.

+

+The effect of assigning (through the use of eval) a variable +bound in a scheme-report-environment +(for example car) is unspecified. Thus the environments specified +by scheme-report-environment may be immutable.

+

+

+ +

+

+

+

+
optional procedure:  (interaction-environment) 
+ +

+This procedure returns a specifier for the environment that +contains implementation-defined bindings, typically a superset of +those listed in the report. The intent is that this procedure +will return the environment in which the implementation would evaluate +expressions dynamically typed by the user.

+

+

+ +

+

+

+ +

6.6  Input and output

+

+ +

6.6.1  Ports

+

+

+Ports represent input and output devices. To Scheme, an input port is a +Scheme object that can deliver characters upon command, while an output port +is a Scheme object that can accept characters.

+

+

+

+

+

+
library procedure:  (call-with-input-file string proc) 
+ +
library procedure:  (call-with-output-file string proc) 
+ +

+String should be a string naming a file, and +proc should be a procedure that accepts one argument. +For call-with-input-file, +the file should already exist; for +call-with-output-file, +the effect is unspecified if the file +already exists. These procedures call proc with one argument: the +port obtained by opening the named file for input or output. If the +file cannot be opened, an error is signalled. If proc returns, +then the port is closed automatically and the value(s) yielded by the +proc is(are) returned. If proc does not return, then +the port will not be closed automatically unless it is possible to +prove that the port will never again be used for a read or write +operation. + +

+
Rationale:   +Because Scheme's escape procedures have unlimited extent, it is +possible to escape from the current continuation but later to escape back in. +If implementations were permitted to close the port on any escape from the +current continuation, then it would be impossible to write portable code using +both call-with-current-continuation and call-with-input-file or +call-with-output-file. + +
+ +

+

+

+

+
procedure:  (input-port? obj) 
+ +
procedure:  (output-port? obj) 
+ +

+Returns #t if obj is an input port or output port +respectively, otherwise returns #f.

+

+

+

+

+ +

+

+

+

+
procedure:  (current-input-port) 
+ +
procedure:  (current-output-port) 
+ +

+Returns the current default input or output port.

+

+

+ +

+

+

+

+
optional procedure:  (with-input-from-file string thunk) 
+ +
optional procedure:  (with-output-to-file string thunk) 
+ +

+String should be a string naming a file, and +proc should be a procedure of no arguments. +For with-input-from-file, +the file should already exist; for +with-output-to-file, +the effect is unspecified if the file +already exists. +The file is opened for input or output, an input or output port +connected to it is made the default value returned by +current-input-port or current-output-port +(and is used by (read), (write obj), and so forth), +and the +thunk is called with no arguments. When the thunk returns, +the port is closed and the previous default is restored. +With-input-from-file and with-output-to-file return(s) the +value(s) yielded by thunk. +If an escape procedure +is used to escape from the continuation of these procedures, their +behavior is implementation dependent.

+

+ + +

+

+

+ +

+

+

+

+
procedure:  (open-input-file filename) 
+ +

+Takes a string naming an existing file and returns an input port capable of +delivering characters from the file. If the file cannot be opened, an error is +signalled.

+

+

+ +

+

+

+

+
procedure:  (open-output-file filename) 
+ +

+Takes a string naming an output file to be created and returns an output +port capable of writing characters to a new file by that name. If the file +cannot be opened, an error is signalled. If a file with the given name +already exists, the effect is unspecified.

+

+

+ +

+

+

+

+
procedure:  (close-input-port port) 
+ +
procedure:  (close-output-port port) 
+ +

+Closes the file associated with port, rendering the port +incapable of delivering or accepting characters. +These routines have no effect if the file has already been closed. +The value returned is unspecified.

+

+

+

+

+

+

+ +

+

+

+ +

6.6.2  Input

+

+

+

+

+

+

+

+

+

+
library procedure:  (read) 
+ +
library procedure:  (read port) 
+ +

+Read converts external representations of Scheme objects into the +objects themselves. That is, it is a parser for the nonterminal +<datum> (see sections 7.1.2 and +6.3.2). Read returns the next +object parsable from the given input port, updating port to point to +the first character past the end of the external representation of the object.

+

+If an end of file is encountered in the input before any +characters are found that can begin an object, then an end of file +object is returned. The port remains open, and further attempts +to read will also return an end of file object. If an end of file is +encountered after the beginning of an object's external representation, +but the external representation is incomplete and therefore not parsable, +an error is signalled.

+

+The port argument may be omitted, in which case it defaults to the +value returned by current-input-port. It is an error to read from +a closed port. +

+ +

+

+

+

+
procedure:  (read-char) 
+ +
procedure:  (read-char port) 
+ +

+Returns the next character available from the input port, updating +the port to point to the following character. If no more characters +are available, an end of file object is returned. Port may be +omitted, in which case it defaults to the value returned by current-input-port.

+

+

+ +

+

+

+

+
procedure:  (peek-char) 
+ +
procedure:  (peek-char port) 
+ +

+Returns the next character available from the input port, +without updating +the port to point to the following character. If no more characters +are available, an end of file object is returned. Port may be +omitted, in which case it defaults to the value returned by current-input-port.

+

+

+
Note:   +The value returned by a call to peek-char is the same as the +value that would have been returned by a call to read-char with the +same port. The only difference is that the very next call to +read-char or peek-char on that port will return the +value returned by the preceding call to peek-char. In particular, a call +to peek-char on an interactive port will hang waiting for input +whenever a call to read-char would have hung. +
+

+

+ +

+

+

+

+
procedure:  (eof-object? obj) 
+ +

+Returns #t if obj is an end of file object, otherwise returns +#f. The precise set of end of file objects will vary among +implementations, but in any case no end of file object will ever be an object +that can be read in using read.

+

+

+ +

+

+

+

+
procedure:  (char-ready?) 
+ +
procedure:  (char-ready? port) 
+ +

+Returns #t if a character is ready on the input port and +returns #f otherwise. If char-ready returns #t then +the next read-char operation on the given port is guaranteed +not to hang. If the port is at end of file then char-ready? +returns #t. Port may be omitted, in which case it defaults to +the value returned by current-input-port.

+

+

+
Rationale:   +Char-ready? exists to make it possible for a program to +accept characters from interactive ports without getting stuck waiting for +input. Any input editors associated with such ports must ensure that +characters whose existence has been asserted by char-ready? cannot +be rubbed out. If char-ready? were to return #f at end of +file, a port at end of file would be indistinguishable from an interactive +port that has no ready characters. +
+ +

+

+

+ +

6.6.3  Output

+

+

+ +

+

+

+

+

+

+
library procedure:  (write obj) 
+ +
library procedure:  (write obj port) 
+ +

+Writes a written representation of obj to the given port. Strings +that appear in the written representation are enclosed in doublequotes, and +within those strings backslash and doublequote characters are +escaped by backslashes. +Character objects are written using the #\ notation. +Write returns an unspecified value. The +port argument may be omitted, in which case it defaults to the value +returned by current-output-port.

+

+

+ +

+

+

+

+
library procedure:  (display obj) 
+ +
library procedure:  (display obj port) 
+ +

+Writes a representation of obj to the given port. Strings +that appear in the written representation are not enclosed in +doublequotes, and no characters are escaped within those strings. Character +objects appear in the representation as if written by write-char +instead of by write. Display returns an unspecified value. +The port argument may be omitted, in which case it defaults to the +value returned by current-output-port.

+

+

+
Rationale:   +Write is intended +for producing machine-readable output and display is for producing +human-readable output. Implementations that allow ``slashification'' +within symbols will probably want write but not display to +slashify funny characters in symbols. +
+ +

+

+

+

+
library procedure:  (newline) 
+ +
library procedure:  (newline port) 
+ +

+Writes an end of line to port. Exactly how this is done differs +from one operating system to another. Returns an unspecified value. +The port argument may be omitted, in which case it defaults to the +value returned by current-output-port.

+

+

+ +

+

+

+

+
procedure:  (write-char char) 
+ +
procedure:  (write-char char port) 
+ +

+Writes the character char (not an external representation of the +character) to the given port and returns an unspecified value. The +port argument may be omitted, in which case it defaults to the value +returned by current-output-port.

+

+

+ +

+

+

+ +

6.6.4  System interface

+

Questions of system interface generally fall outside of the domain of this +report. However, the following operations are important enough to +deserve description here.

+

+

+

+
optional procedure:  (load filename) 
+ +

+

+

+Filename should be a string naming an existing file +containing Scheme source code. The load procedure reads +expressions and definitions from the file and evaluates them +sequentially. It is unspecified whether the results of the expressions +are printed. The load procedure does not affect the values +returned by current-input-port and current-output-port. +Load returns an unspecified value.

+

+

+
Rationale:   +For portability, load must operate on source files. +Its operation on other kinds of files necessarily varies among +implementations. +
+ +

+

+

+

+
optional procedure:  (transcript-on filename) 
+ +
optional procedure:  (transcript-off) 
+ +

+Filename must be a string naming an output file to be +created. The effect of transcript-on is to open the named file +for output, and to cause a transcript of subsequent interaction between +the user and the Scheme system to be written to the file. The +transcript is ended by a call to transcript-off, which closes the +transcript file. Only one transcript may be in progress at any time, +though some implementations may relax this restriction. The values +returned by these procedures are unspecified.

+

+

+ +

+        

+

+ +

+ + diff --git a/doc/r5rs-std/r5rs.html b/doc/r5rs-std/r5rs.html new file mode 100644 index 0000000000..54b4a36d2a --- /dev/null +++ b/doc/r5rs-std/r5rs.html @@ -0,0 +1,343 @@ + + + + + +Revised^5 Report on the Algorithmic Language Scheme + + + + + + + + + +

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+ +

+

+ + + + + + + + +

+

+

+

+ + + + + + +

+

+ +

+

+ +

+

+

+

+

+

+ +

+

+ + +

+

+ + + + + +

+

+

+

+

+

+ + + + + + + + + + +

+

+ + + + + + +

+

+ +

+

+ +

+

+

+

+ +

+

+ + + + +

+

+ + +

+

+ + +

+

+

+

+

+

+

+

+

+

+

+

+

+

+ +

+

+

+

+

+

+ +

+

+ +

+

+ +

+

+

+

+

+

+

+

+

+

+

+

+ +

+

+

+

+ +

+

+

+

+ +

+

+

+

+

+

+

+

+

+

+

+

+

+

+ +

+

+ + + + + + + +

+

+

+

+

+

+

+

+

+

+

+

+ +

+

+

+
+



Revised5 Report on the Algorithmic Language Scheme

+

+20 February 1998
+
+Richard Kelsey, William Clinger, and Jonathan Rees (Editors)

+ + + + + +
H. Abelson R. K. Dybvig C. T. Haynes G. J. Rozas
N. I. Adams IV D. P. Friedman E. Kohlbecker G. L. Steele Jr.
D. H. Bartley R. Halstead D. Oxley G. J. Sussman
G. Brooks C. Hanson K. M. Pitman M. Wand

+Dedicated to the Memory of Robert Hieb +

+

+

+

+

+ +

+

+ +

+

+ + +

+

+

+

+

+

+

+

+

+

+

+

+

+

+ + + +

+

+ +

+

+ + +

+

+ +

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+ +

+

+

+

+ + + + +

+

+ +

+

+

+ +

+ + diff --git a/doc/srfi-std/index.html b/doc/srfi-std/index.html new file mode 100644 index 0000000000..dba609f75f --- /dev/null +++ b/doc/srfi-std/index.html @@ -0,0 +1,258 @@ + + + + SRFI documents inside PLT + + + +

SRFI documents inside PLT

+ +

Scheme Requests for Implementation

+ +

The "Scheme Requests for Implementation" (SRFI) process + is a new approach to helping Scheme users to write portable and + yet useful code. It is a forum for people interested in + coordinating libraries and other additions to the Scheme language + between implementations.

+ +

This manual includes the original documentation of all + the SRFIs already ported to PLT Scheme. For more information on + the SRFI process, please follow + this link.

+ + +

Loading

+ +

To load a SRFI with name conflicts (currently SRFIs 1, 5, 13, 17, 19, 43, + 45, 48, 61, 63, 69 and 87) in a module,
+ please see the note below.
+
+ To load a SRFI, use the following form:
+
+     (require (lib "N.ss" "srfi"))
+
+ if you know the number of the SRFI you want to load. This is the preferred + method, or this one:
+
+     (require (lib "NAME.ss" "srfi" "N"))
+
+ if you know the `informative name' of the SRFI.
+
+ N, is a number corresponding to the sub-collection that holds a particular + SRFI, and NAME is a more descriptive name we assigned to the main file in + which the SRFI is defined. For instance, to load SRFI-34 you have to do + either one of:
+
+     (require (lib "34.ss" "srfi"))
+
+ or,
+
+     (require (lib "exception.ss" "srfi" "34"))
+
+ ¡¡

+ + +

+ NOTE on SRFIs with name conflicts
+

+ Certain SRFIs (currently SRFIs 1, 5, 13, 17, 19, 43, 45, 48, 61, 63, 69 and + 87) provide names which conflict with names provided by the 'mzscheme' + language. Attempting to require one of these SRFIs in a module written in + the 'mzscheme' language will result in an error.
+
+ To address this problem, the PLT implementations of these SRFIs provide a + different module which renames the problematic exports to avoid these + conflicts. For SRFI 1, this library is called list.ss, and should be + required like this:
+
+     (require (lib "list.ss" "srfi" "1"))
+
+ which supplies the colliding names with a prefix of 's:' (e.g. "s:map", + "s:reverse!") and is therefore suitable for requires in a module.
+
+ For SRFI 19, this library is called time.ss, and should be required like + this:
+
+     (require (lib "time.ss" "srfi" "19"))
+
+ which supplies the colliding names with a prefix of 'srfi:' (e.g. + "srfi:date?", "srfi:date-second") and is therefore
+ suitable for requires in a module.
+
+ ¡¡

+ + +

+ Supported SRFIs
+ ¡¡

+ + +

SRFI           File + name           + Sub-collection
+ SRFI-1         list.ss             + 1
+ SRFI-2         and-let.ss          + 2
+ SRFI-4(*1)     4.ss
+ SRFI-5         let.ss              + 5
+ SRFI-6(+)      6.ss
+ SRFI-7         program.ss          + 7
+ SRFI-8         receive.ss          + 8
+ SRFI-9         record.ss           + 9
+ SRFI-11(+)     11.ss
+ SRFI-13        string.ss           + 13
+ SRFI-14        char-set.ss         + 14
+ SRFI-16(+)     16.ss
+ SRFI-17        set.ss              + 17
+ SRFI-19(*2)    time.ss             + 19
+ SRFI-23(+)     23.ss
+ SRFI-25        array.ss            + 25
+ SRFI-26        cut.ss              + 26
+ SRFI-27        random-bits.ss      + 27
+ SRFI-28(+)     28.ss
+ SRFI-29        localization.ss     + 29
+ SRFI-30(+)     30.ss
+ SRFI-31        rec.ss              + 31
+ SRFI-32        sort.scm            + 32
+ SRFI-34        exception.ss        + 34
+ SRFI-35        condition.ss        + 35
+ SRFI-38(+)     38.ss
+ SRFI-39(+)     39.ss
+ SRFI-40        stream.ss           + 40
+ SRFI-42        comprehensions.ss   + 42
+ SRFI-43        vector-lib.ss       + 43
+ SRFI-45(*3)    lazy.ss             + 45
+ SRFI-48        format.ss           + 48
+ SRFI-54        cat.ss              + 54
+ SRFI-57        records.ss          + 57
+ SRFI-59        vicinity.ss         + 59
+ SRFI-60        60.ss               + 60
+ SRFI-61        cond.ss             + 61
+ SRFI-62(+)
+ SRFI-63        63.ss               + 63
+ SRFI-64        testing.ss          + 64
+ SRFI-66        66.ss               + 66
+ SRFI-67        compare.ss          + 67
+ SRFI-69        hash.ss             + 69
+ SRFI-71        letvalues.ss        + 71
+ SRFI-74        74.ss               + 74
+ SRFI-78        check.ss            + 78
+ SRFI-86        86.ss               + 86
+ SRFI-87        case.ss             + 87
+¡¡

+ + +

Notes:

+ + +

+ Supported by the core of PLT Scheme
+
+ *1 The functionality is all part of mzscheme available + via (lib"foreign.ss"), the only missing part is the i/o + syntax.
+
+ *2 The time module does not export its time structure + (you have to use the time-* procedures.) It renames all + the date-* accessors to tm:date-* so that you won't get + errors when including this code in other modules. Care + most be taken NOT to confuse the internal date structure + with the PLT Scheme one, they are not the same, and all + procedures from this library expect the former.
+
+ *3 This port also provides promise? / srfi-45-promise?.
+¡¡

+ + +

Ported SRFIs: original documents

+ + + + + \ No newline at end of file diff --git a/doc/srfi-std/srfi-1.html b/doc/srfi-std/srfi-1.html new file mode 100644 index 0000000000..df91deb529 --- /dev/null +++ b/doc/srfi-std/srfi-1.html @@ -0,0 +1,3246 @@ + + + + + + + + + + SRFI 1: List Library + + + + + + + + + + +

Title

+SRFI-1: List Library +
+ + +

Author

+ +Olin Shivers + +
+ http://www.ai.mit.edu/~shivers/ / + shivers@ai.mit.edu +
+ + +

Status

+ +This SRFI is currently in ``final'' status. To see an explanation of each status that a SRFI can hold, see here. +You can access the discussion via the archive of the mailing list. +

+

+ + +

Table of contents

+ + + + + + +

Abstract

+

+R5RS Scheme has an impoverished set of list-processing utilities, which is a +problem for authors of portable code. This SRFI proposes a coherent and +comprehensive set of list-processing procedures; it is accompanied by a +reference implementation of the spec. The reference implementation is +

+ + +

Rationale

+

+The set of basic list and pair operations provided by R4RS/R5RS Scheme is far +from satisfactory. Because this set is so small and basic, most +implementations provide additional utilities, such as a list-filtering +function, or a "left fold" operator, and so forth. But, of course, this +introduces incompatibilities -- different Scheme implementations provide +different sets of procedures. + +

+I have designed a full-featured library of procedures for list processing. +While putting this library together, I checked as many Schemes as I could get +my hands on. (I have a fair amount of experience with several of these +already.) I missed Chez -- no on-line manual that I can find -- but I hit most +of the other big, full-featured Schemes. The complete list of list-processing +systems I checked is: +

+ R4RS/R5RS Scheme, MIT Scheme, Gambit, RScheme, MzScheme, slib, + Common Lisp, Bigloo, guile, T, APL and the SML standard basis +
+

+As a result, the library I am proposing is fairly rich. +

+Following this initial design phase, this library went through several +months of discussion on the SRFI mailing lists, and was altered in light +of the ideas and suggestions put forth during this discussion. +

+In parallel with designing this API, I have also written a reference +implementation. I have placed this source on the Net with an unencumbered, +"open" copyright. A few notes about the reference implementation: + +

+

+In short, I've written the reference implementation to make it as painless +as possible for an implementor -- or a regular programmer -- to adopt this +library and get good results with it. + + + +

Procedure Index

+

+Here is a short list of the procedures provided by the list-lib package. +R5RS procedures are shown in +bold; +extended R5RS + procedures, in bold italic. +

+
+
Constructors +
+
+cons list
+xcons cons* make-list list-tabulate 
+list-copy circular-list iota
+
+ +
Predicates +
+
+pair? null?
+proper-list? circular-list? dotted-list? 
+not-pair? null-list?
+list=
+
+ +
Selectors +
+
+car cdr ... cddadr cddddr list-ref
+first second third fourth fifth sixth seventh eighth ninth tenth
+car+cdr
+take       drop
+take-right drop-right
+take!      drop-right! 
+split-at   split-at! 
+last last-pair
+
+ +
Miscellaneous: length, append, concatenate, reverse, zip & count +
+
+length length+
+append  concatenate  reverse
+append! concatenate! reverse!
+append-reverse append-reverse!
+zip unzip1 unzip2 unzip3 unzip4 unzip5
+count
+
+ +
Fold, unfold & map +
+
+map for-each
+fold       unfold       pair-fold       reduce 
+fold-right unfold-right pair-fold-right reduce-right 
+append-map append-map!
+map! pair-for-each filter-map map-in-order
+
+ +
Filtering & partitioning +
+
+filter  partition  remove
+filter! partition! remove! 
+
+ +
Searching +
+
+member memq memv
+find find-tail 
+any every
+list-index
+take-while drop-while take-while!
+span break span! break!
+
+ +
Deleting +
+
+delete  delete-duplicates 
+delete! delete-duplicates!
+
+ +
Association lists +
+
+assoc assq assv
+alist-cons alist-copy
+alist-delete alist-delete!
+
+ +
Set operations on lists +
+
+lset<= lset= lset-adjoin
+lset-union			lset-union!
+lset-intersection		lset-intersection!
+lset-difference		        lset-difference!
+lset-xor			lset-xor!
+lset-diff+intersection	        lset-diff+intersection!
+
+ +
Primitive side-effects +
+
+set-car! set-cdr!
+
+
+
+ +

+Four R4RS/R5RS list-processing procedures are extended by this library in +backwards-compatible ways: +

+ +
map for-each + (Extended to take lists of unequal length) +
member assoc + (Extended to take an optional comparison procedure.) +
+
+ +

+The following R4RS/R5RS list- and pair-processing procedures are also part of +list-lib's exports, as defined by the R5RS: +

+
+cons pair? null?
+car cdr ... cdddar cddddr 
+set-car! set-cdr! 
+list append reverse
+length list-ref
+memq memv assq assv
+
+
+ +

+The remaining two R4RS/R5RS list-processing +procedures are not part of +this library: +

+ +
list-tail + (renamed drop) +
list? + (see proper-list?, + circular-list? and + dotted-list?) +
+
+ + +

General discussion

+

+ +A set of general criteria guided the design of this library. + +

+ +I don't require "destructive" (what I call "linear update") procedures to +alter and recycle cons cells from the argument lists. They are allowed to, but +not required to. (And the reference implementations I have written do +recycle the argument lists.) + +

+List-filtering procedures such as filter or delete do not disorder +lists. Elements appear in the answer list in the same order as they appear in +the argument list. This constrains implementation, but seems like a desirable +feature, since in many uses of lists, order matters. (In particular, +disordering an alist is definitely a bad idea.) +

+Contrariwise, although the reference implementations of the list-filtering +procedures share longest common tails between argument and answer lists, +it not is part of the spec. +

+Because lists are an inherently sequential data structure (unlike, say, +vectors), list-inspection functions such as find, find-tail, for-each, any +and every commit to a left-to-right traversal order of their argument list. +

+However, constructor functions, such as list-tabulate and the mapping +procedures (append-map, append-map!, map!, pair-for-each, filter-map, +map-in-order), do not specify the dynamic order in which their procedural +argument is applied to its various values. +

+Predicates return useful true values wherever possible. Thus any must return +the true value produced by its predicate, and every returns the final true +value produced by applying its predicate argument to the last element of its +argument list. +

+Functionality is provided both in pure and linear-update (potentially +destructive) forms wherever this makes sense. +

+No special status accorded Scheme's built-in equality functions. +Any functionality provided in terms of eq?, eqv?, equal? is also +available using a client-provided equality function. +

+Proper design counts for more than backwards compatibility, but I have tried, +ceteris paribus, +to be as backwards-compatible as possible with existing +list-processing libraries, in order to facilitate porting old code to run as a +client of the procedures in this library. Name choices and semantics are, for +the most part, in agreement with existing practice in many current Scheme +systems. I have indicated some incompatibilities in the following text. +

+These procedures are not "sequence generic" -- i.e., procedures that +operate on either vectors and lists. They are list-specific. I prefer to +keep the library simple and focussed. +

+I have named these procedures without a qualifying initial "list-" lexeme, +which is in keeping with the existing set of list-processing utilities in +Scheme. +I follow the general Scheme convention (vector-length, string-ref) of +placing the type-name before the action when naming procedures -- so +we have list-copy and pair-for-each rather than the perhaps +more fluid, but less consistent, copy-list or for-each-pair. +

+I have generally followed a regular and consistent naming scheme, composing +procedure names from a set of basic lexemes. + + +

"Linear update" procedures

+

+ +Many procedures in this library have "pure" and "linear update" variants. A +"pure" procedure has no side-effects, and in particular does not alter its +arguments in any way. A "linear update" procedure is allowed -- but not +required -- to side-effect its arguments in order to construct its +result. "Linear update" procedures are typically given names ending with an +exclamation point. So, for example, (append! list1 list2) is allowed to +construct its result by simply using set-cdr! to set the cdr of the last pair +of list1 to point to list2, and then returning list1 (unless list1 is the +empty list, in which case it would simply return list2). However, append! may +also elect to perform a pure append operation -- this is a legal definition +of append!: +

+(define append! append)
+
+

+This is why we do not call these procedures "destructive" -- because they +aren't required to be destructive. They are potentially destructive. +

+What this means is that you may only apply linear-update procedures to +values that you know are "dead" -- values that will never be used again +in your program. This must be so, since you can't rely on the value passed +to a linear-update procedure after that procedure has been called. It +might be unchanged; it might be altered. +

+The "linear" in "linear update" doesn't mean "linear time" or "linear space" +or any sort of multiple-of-n kind of meaning. It's a fancy term that +type theorists and pure functional programmers use to describe +systems where you are only allowed to have exactly one reference to each +variable. This provides a guarantee that the value bound to a variable is +bound to no other variable. So when you use a variable in a variable +reference, you "use it up." Knowing that no one else has a pointer to that +value means the a system primitive is free to side-effect its arguments to +produce what is, observationally, a pure-functional result. +

+In the context of this library, "linear update" means you, the programmer, +know there are no other live references to the value passed to the +procedure -- after passing the value to one of these procedures, the +value of the old pointer is indeterminate. Basically, you are licensing +the Scheme implementation to alter the data structure if it feels like +it -- you have declared you don't care either way. +

+You get no help from Scheme in checking that the values you claim are "linear" +really are. So you better get it right. Or play it safe and use the non-! +procedures -- it doesn't do any good to compute quickly if you get the wrong +answer. +

+Why go to all this trouble to define the notion of "linear update" and use it +in a procedure spec, instead of the more common notion of a "destructive" +operation? First, note that destructive list-processing procedures are almost +always used in a linear-update fashion. This is in part required by the +special case of operating upon the empty list, which can't be side-effected. +This means that destructive operators are not pure side-effects -- they have +to return a result. Second, note that code written using linear-update +operators can be trivially ported to a pure, functional subset of Scheme by +simply providing pure implementations of the linear-update operators. Finally, +requiring destructive side-effects ruins opportunities to parallelise these +operations -- and the places where one has taken the trouble to spell out +destructive operations are usually exactly the code one would want a +parallelising compiler to parallelise: the efficiency-critical kernels of the +algorithm. Linear-update operations are easily parallelised. Going with a +linear-update spec doesn't close off these valuable alternative implementation +techniques. This list library is intended as a set of low-level, basic +operators, so we don't want to exclude these possible implementations. +

+The linear-update procedures in this library are +

+take! drop-right! split-at! +append! concatenate! reverse! append-reverse! +append-map! map! +filter! partition! remove! +take-while! span! break! +delete! alist-delete! delete-duplicates! +lset-adjoin! lset-union! lset-intersection! +lset-difference! lset-xor! lset-diff+intersection! +
+ + + +

Improper Lists

+

+ +Scheme does not properly have a list type, just as C does not have a string +type. Rather, Scheme has a binary-tuple type, from which one can build binary +trees. There is an interpretation of Scheme values that allows one to +treat these trees as lists. Further complications ensue from the fact that +Scheme allows side-effects to these tuples, raising the possibility of lists +of unbounded length, and trees of unbounded depth (that is, circular data +structures). + +

+However, there is a simple view of the world of Scheme values that considers +every value to be a list of some sort. that is, every value is either +

+

+Note that the zero-length dotted lists are simply all the non-null, non-pair +values. + +

+This view is captured by the predicates proper-list?, dotted-list?, and +circular-list?. List-lib users should note that dotted lists are not commonly +used, and are considered by many Scheme programmers to be an ugly artifact of +Scheme's lack of a true list type. However, dotted lists do play a noticeable +role in the syntax of Scheme, in the "rest" parameters used by n-ary +lambdas: (lambda (x y . rest) ...). + +

+Dotted lists are not fully supported by list-lib. Most procedures are +defined only on proper lists -- that is, finite, nil-terminated lists. The +procedures that will also handle circular or dotted lists are specifically +marked. While this design decision restricts the domain of possible arguments +one can pass to these procedures, it has the benefit of allowing the +procedures to catch the error cases where programmers inadvertently pass +scalar values to a list procedure by accident, +e.g., by switching the arguments to a procedure call. + + +

Errors

+

+ +Note that statements of the form "it is an error" merely mean "don't +do that." They are not a guarantee that a conforming implementation will +"catch" such improper use by, for example, raising some kind of exception. +Regrettably, R5RS Scheme requires no firmer guarantee even for basic operators such +as car and cdr, so there's little point in requiring these procedures to do +more. Here is the relevant section of the R5RS: +

+

+ When speaking of an error situation, this report uses the phrase "an + error is signalled" to indicate that implementations must detect and + report the error. If such wording does not appear in the discussion + of an error, then implementations are not required to detect or + report the error, though they are encouraged to do so. An error + situation that implementations are not required to detect is usually + referred to simply as "an error." +

+ For example, it is an error for a procedure to be passed an argument + that the procedure is not explicitly specified to handle, even though + such domain errors are seldom mentioned in this report. + Implementations may extend a procedure's domain of definition to + include such arguments. +

+ + + +

Not included in this library

+

+The following items are not in this library: +

+

+They should have their own SRFI specs. +

+ + + +

The procedures

+

+ +In a Scheme system that has a module or package system, these procedures +should be contained in a module named "list-lib". + +The templates given below obey the following conventions for procedure formals: + + +
list + A proper (finite, nil-terminated) list +
clist + A proper or circular list +
flist + A finite (proper or dotted) list +
pair + A pair +
x, y, d, a + Any value +
object, value + Any value +
n, i + A natural number (an integer >= 0) +
proc + A procedure +
pred + A procedure whose return value is treated as a boolean +
= + A boolean procedure taking two arguments +
+ +

+It is an error to pass a circular or dotted list to a procedure not +defined to accept such an argument. + + +

Constructors

+

+ +

+ + +
+ +cons a d -> pair +
+ [R5RS] + The primitive constructor. Returns a newly allocated pair whose car is + a and whose cdr is d. + The pair is guaranteed to be different (in the sense of eqv?) + from every existing object. +
+(cons 'a '())        => (a)
+(cons '(a) '(b c d)) => ((a) b c d)
+(cons "a" '(b c))    => ("a" b c)
+(cons 'a 3)          => (a . 3)
+(cons '(a b) 'c)     => ((a b) . c)
+
+ + +
+ +list object ... -> list +
+ [R5RS] + Returns a newly allocated list of its arguments. +
+(list 'a (+ 3 4) 'c) =>  (a 7 c)
+(list)               =>  ()
+
+ + +
+ +xcons d a -> pair +
+
+(lambda (d a) (cons a d))
+
+ Of utility only as a value to be conveniently passed to higher-order + procedures. + +
+(xcons '(b c) 'a) => (a b c)
+
+ + The name stands for "eXchanged CONS." + + + +
cons* elt1 elt2 ... -> object +
+ + Like list, + but the last argument provides the tail of the constructed list, + returning +
+(cons elt1 (cons elt2 (cons ... eltn))) +
+ This function is called list* in Common Lisp and about + half of the Schemes that provide it, + and cons* in the other half. +
+(cons* 1 2 3 4) => (1 2 3 . 4)
+(cons* 1) => 1
+
+ + + +
make-list n [fill] -> list +
+ Returns an n-element list, + whose elements are all the value fill. + If the fill argument is not given, the elements of the list may + be arbitrary values. +
+(make-list 4 'c) => (c c c c)
+
+ + + +
list-tabulate n init-proc -> list +
+ Returns an n-element list. Element i of the list, where 0 <= i < n, + is produced by (init-proc i). No guarantee is made about the dynamic + order in which init-proc is applied to these indices. + +
+(list-tabulate 4 values) => (0 1 2 3)
+
+ + + +
list-copy flist -> flist +
+ Copies the spine of the argument. + + + +
circular-list elt1 elt2 ... -> list +
+ Constructs a circular list of the elements. +
+(circular-list 'z 'q) => (z q z q z q ...)
+
+ + + +
iota count [start step] -> list +
+ Returns a list containing the elements +
+(start start+step ... start+(count-1)*step)
+
+ The start and step parameters default to 0 and 1, respectively. + This procedure takes its name from the APL primitive. + +
+(iota 5) => (0 1 2 3 4)
+(iota 5 0 -0.1) => (0 -0.1 -0.2 -0.3 -0.4)
+
+
+ + +

Predicates

+

+Note: the predicates proper-list?, circular-list?, and dotted-list? +partition the entire universe of Scheme values. + +

+ +
+proper-list? x -> boolean + +
+ Returns true iff x is a proper list -- a finite, nil-terminated list. +

+ More carefully: The empty list is a proper list. A pair whose cdr is a + proper list is also a proper list: +

+<proper-list> ::= ()                            (Empty proper list)
+              |   (cons <x> <proper-list>)      (Proper-list pair)
+
+ Note that this definition rules out circular lists. This + function is required to detect this case and return false. +

+ Nil-terminated lists are called "proper" lists by R5RS and Common Lisp. + The opposite of proper is improper. +

+ R5RS binds this function to the variable list?. +

+

+(not (proper-list? x)) = (or (dotted-list? x) (circular-list? x))
+
+ + + +
circular-list? x -> boolean +
+ True if x is a circular list. A circular list is a value such that + for every n >= 0, cdrn(x) is a pair. +

+ Terminology: The opposite of circular is finite. +

+(not (circular-list? x)) = (or (proper-list? x) (dotted-list? x))
+
+ + + +
dotted-list? x -> boolean +
+ True if x is a finite, non-nil-terminated list. That is, there exists + an n >= 0 such that cdrn(x) is neither a pair nor (). + This includes + non-pair, non-() values (e.g. symbols, numbers), + which are considered to be dotted lists of length 0. +
+(not (dotted-list? x)) = (or (proper-list? x) (circular-list? x))
+
+ + + +
pair? object -> boolean +
+ [R5RS] + Returns #t if object is a pair; otherwise, #f. +
+(pair? '(a . b)) =>  #t
+(pair? '(a b c)) =>  #t
+(pair? '())      =>  #f
+(pair? '#(a b))  =>  #f
+(pair? 7)        =>  #f
+(pair? 'a)       =>  #f
+
+ + + +
null? object -> boolean +
+ [R5RS] + Returns #t if object is the empty list; otherwise, #f. + + + +
null-list? list -> boolean +
+ List is a proper or circular list. This procedure returns true if + the argument is the empty list (), and false otherwise. It is an + error to pass this procedure a value which is not a proper or + circular list. + + This procedure is recommended as the termination condition for + list-processing procedures that are not defined on dotted lists. + + +
+ +not-pair? x -> boolean +
+
(lambda (x) (not (pair? x)))
+ Provided as a procedure as it can be useful as the termination condition + for list-processing procedures that wish to handle all finite lists, + both proper and dotted. + + +
+ +list= elt= list1 ... -> boolean +
+ Determines list equality, given an element-equality procedure. + Proper list A equals proper list B + if they are of the same length, + and their corresponding elements are equal, + as determined by elt=. + If the element-comparison procedure's first argument is + from listi, + then its second argument is from listi+1, + i.e. it is always called as + (elt= a b) + for a an element of list A, + and b an element of list B. +

+ In the n-ary case, + every listi is compared to + listi+1 + (as opposed, for example, to comparing + list1 to every listi, + for i>1). + If there are no list arguments at all, + list= simply returns true. +

+ It is an error to apply list= to anything except proper lists. + While + implementations may choose to extend it to circular lists, note that it + cannot reasonably be extended to dotted lists, as it provides no way to + specify an equality procedure for comparing the list terminators. +

+ Note that the dynamic order in which the elt= procedure is + applied to pairs of elements is not specified. + For example, if list= is applied + to three lists, A, B, and C, + it may first completely compare A to B, + then compare B to C, + or it may compare the first elements of A and B, + then the first elements of B and C, + then the second elements of A and B, and so forth. +

+ The equality procedure must be consistent with eq?. + That is, it must be the case that +

+ (eq? x y) => (elt= x y). +
+ Note that this implies that two lists which are eq? + are always list=, as well; implementations may exploit this + fact to "short-cut" the element-by-element comparisons. +
+(list= eq?) => #t       ; Trivial cases
+(list= eq? '(a)) => #t
+
+ +
+ + + +

Selectors

+
+ + + + +
car pair -> value +
cdr pair -> value +
+ [R5RS] + These functions return the contents of the car and cdr field of their + argument, respectively. + Note that it is an error to apply them to the empty list. +
+(car '(a b c))     =>  a             (cdr '(a b c))     =>  (b c)  
+(car '((a) b c d)) =>  (a)	     (cdr '((a) b c d)) =>  (b c d)
+(car '(1 . 2))     =>  1	     (cdr '(1 . 2))     =>  2      
+(car '())          =>  *error*	     (cdr '())          =>  *error*
+
+ + + + + + + + +
caar pair -> value +
cadr pair -> value +
: +
cdddar pair -> value +
cddddr pair -> value +
+ [R5RS] + These procedures are compositions of car and cdr, + where for example caddr could be defined by +
    
+(define caddr (lambda (x) (car (cdr (cdr x))))).
+
+ Arbitrary compositions, up to four deep, are provided. There are + twenty-eight of these procedures in all. + + + +
list-ref clist i -> value +
+ [R5RS] + Returns the ith element of clist. + (This is the same as the car of + (drop clist i).) + It is an error if i >= n, + where n is the length of clist. +
    
+(list-ref '(a b c d) 2) => c
+
+ + +
+ +first   pair -> object +
+ +second  pair -> object +
+ +third   pair -> object +
+ +fourth  pair -> object +
+ +fifth   pair -> object +
+ +sixth   pair -> object +
+ +seventh pair -> object +
+ +eighth  pair -> object +
+ +ninth   pair -> object +
+ +tenth   pair -> object +
+ Synonyms for car, cadr, caddr, ... + +
+(third '(a b c d e)) => c
+
+ + +
+ +car+cdr pair -> [x y] +
+ The fundamental pair deconstructor: +
+(lambda (p) (values (car p) (cdr p)))
+
+ This can, of course, be implemented more efficiently by a compiler. + + +
+ +take x i -> list +
+ +drop x i -> object +
+ take returns the first i elements of list x.
+ drop returns all but the first i elements of list x. +
+(take '(a b c d e)  2) => (a b)
+(drop '(a b c d e)  2) => (c d e)
+
+ x may be any value -- a proper, circular, or dotted list: +
+(take '(1 2 3 . d) 2) => (1 2)
+(drop '(1 2 3 . d) 2) => (3 . d)
+(take '(1 2 3 . d) 3) => (1 2 3)
+(drop '(1 2 3 . d) 3) => d
+
+ For a legal i, take and drop partition the list in a manner which + can be inverted with append: +
+(append (take x i) (drop x i)) = x
+
+ drop is exactly equivalent to performing i cdr operations on x; + the returned value shares a common tail with x. + + If the argument is a list of non-zero length, take is guaranteed to + return a freshly-allocated list, even in the case where the entire + list is taken, e.g. (take lis (length lis)). + + +
+ +take-right flist i -> object +
+ +drop-right flist i -> list +
+ take-right returns the last i elements of flist.
+ drop-right returns all but the last i elements of flist. +
+(take-right '(a b c d e) 2) => (d e)
+(drop-right '(a b c d e) 2) => (a b c)
+
+ The returned list may share a common tail with the argument list. +

+ flist may be any finite list, either proper or dotted: +

+(take-right '(1 2 3 . d) 2) => (2 3 . d)
+(drop-right '(1 2 3 . d) 2) => (1)
+(take-right '(1 2 3 . d) 0) => d
+(drop-right '(1 2 3 . d) 0) => (1 2 3)
+
+ For a legal i, take-right and drop-right partition the list in a manner + which can be inverted with append: +
+(append (take flist i) (drop flist i)) = flist
+
+ take-right's return value is guaranteed to share a common tail with flist. + + If the argument is a list of non-zero length, drop-right is guaranteed to + return a freshly-allocated list, even in the case where nothing is + dropped, e.g. (drop-right lis 0). + + +
+ +take! x i -> list +
+ +drop-right! flist i -> list +
+ take! and drop-right! are "linear-update" variants of take and + drop-right: the procedure is allowed, but not required, to alter the + argument list to produce the result. +

+ If x is circular, take! may return a shorter-than-expected list: +

+(take! (circular-list 1 3 5) 8) => (1 3)
+(take! (circular-list 1 3 5) 8) => (1 3 5 1 3 5 1 3)
+
+ + + +
+ +split-at  x i -> [list object] +
+ +split-at! x i -> [list object] +
+ split-at splits the list x + at index i, returning a list of the + first i elements, and the remaining tail. It is equivalent + to +
+(values (take x i) (drop x i))
+
+ split-at! is the linear-update variant. It is allowed, but not + required, to alter the argument list to produce the result. +
+(split-at '(a b c d e f g h) 3) =>
+    (a b c)
+    (d e f g h)
+
+ + + +
+ +last pair -> object +
+ +last-pair pair -> pair +
+ last returns the last element of the non-empty, + finite list pair. + last-pair returns the last pair in the non-empty, + finite list pair. + +
+(last '(a b c)) => c
+(last-pair '(a b c)) => (c)
+
+ +
+ + +

Miscellaneous: length, append, concatenate, reverse, zip & count

+ +
+ +
+ +length  list -> integer +
+ +length+ clist -> integer or #f +
+ Both length and length+ return the length of the argument. + It is an error to pass a value to length which is not a proper + list (finite and nil-terminated). In particular, this means an + implementation may diverge or signal an error when length is + applied to a circular list. +

+ length+, on the other hand, returns #F when applied to a circular + list. +

+ The length of a proper list is a non-negative integer n such that cdr + applied n times to the list produces the empty list. + + + +

+ +append  list1 ... -> list +
+ +append! list1 ... -> list +
+ [R5RS] + append returns a list consisting of the elements + of list1 + followed by the elements of the other list parameters. +
+(append '(x) '(y))        =>  (x y)
+(append '(a) '(b c d))    =>  (a b c d)
+(append '(a (b)) '((c)))  =>  (a (b) (c))
+
+ The resulting list is always newly allocated, except that it + shares structure with the final listi argument. + This last argument may be any value at all; + an improper list results if it is not + a proper list. All other arguments must be proper lists. +
+(append '(a b) '(c . d))  =>  (a b c . d)
+(append '() 'a)           =>  a
+(append '(x y))           =>  (x y)
+(append)                  =>  ()
+
+ + append! is the "linear-update" variant of append + -- it is allowed, but not required, to alter cons cells in the argument + lists to construct the result list. + The last argument is never altered; the result + list shares structure with this parameter. + + +
+ +concatenate  list-of-lists -> value +
+ +concatenate! list-of-lists -> value +
+ These functions append the elements of their argument together. + That is, concatenate returns +
+(apply append list-of-lists)
+
+ or, equivalently, +
+(reduce-right append '() list-of-lists)
+
+ + concatenate! is the linear-update variant, defined in + terms of append! instead of append. + +

+ Note that some Scheme implementations do not support passing more than a + certain number (e.g., 64) of arguments to an n-ary procedure. + In these implementations, the (apply append ...) idiom + would fail when applied to long lists, + but concatenate would continue to function properly. + +

+ As with append and append!, + the last element of the input list may be any value at all. + + +

+ +reverse  list -> list +
+ +reverse! list -> list +
+ [R5RS] + + reverse returns a newly allocated list consisting of + the elements of list in reverse order. +
+(reverse '(a b c)) =>  (c b a)
+(reverse '(a (b c) d (e (f))))
+    =>  ((e (f)) d (b c) a)
+
+ reverse! is the linear-update variant of reverse. + It is permitted, but not required, to alter the argument's cons cells + to produce the reversed list. + + + +
+ +append-reverse  rev-head tail -> list +
+ +append-reverse! rev-head tail -> list +
+ append-reverse returns + (append (reverse rev-head) tail). + It is provided because it is a common operation -- a common + list-processing style calls for this exact operation to transfer values + accumulated in reverse order onto the front of another list, and because + the implementation is significantly more efficient than the simple + composition it replaces. (But note that this pattern of iterative + computation followed by a reverse can frequently be rewritten as a + recursion, dispensing with the reverse and append-reverse steps, and + shifting temporary, intermediate storage from the heap to the stack, + which is typically a win for reasons of cache locality and eager storage + reclamation.) +

+ append-reverse! is just the linear-update variant -- it is allowed, but + not required, to alter rev-head's cons cells to construct the result. + + + +

zip clist1 clist2 ... -> list +
+
(lambda lists (apply map list lists))
+
+ If zip is passed n lists, it returns a list as long as the shortest + of these lists, each element of which is an n-element list comprised + of the corresponding elements from the parameter lists. + +
+(zip '(one two three) 
+     '(1 2 3)
+     '(odd even odd even odd even odd even))
+    => ((one 1 odd) (two 2 even) (three 3 odd))
+
+(zip '(1 2 3)) => ((1) (2) (3))
+
+ At least one of the argument lists must be finite: +
+(zip '(3 1 4 1) (circular-list #f #t)) 
+    => ((3 #f) (1 #t) (4 #f) (1 #t))
+
+ + + +
unzip1 list -> list + +
unzip2 list -> [list list] + +
unzip3 list -> [list list list] + +
unzip4 list -> [list list list list] + +
unzip5 list -> [list list list list list] +
+ unzip1 takes a list of lists, + where every list must contain at least one element, + and returns a list containing the initial element of each such list. + That is, it returns (map car lists). + unzip2 takes a list of lists, where every list must contain at least + two elements, and returns two values: a list of the first elements, + and a list of the second elements. unzip3 does the same for the first + three elements of the lists, and so forth. + +
+(unzip2 '((1 one) (2 two) (3 three))) =>
+    (1 2 3) 
+    (one two three)
+
+ + +
+ +count pred clist1 clist2 -> integer +
+ pred is a procedure taking as many arguments as there + are lists and returning a single value. It is applied + element-wise to the elements of the lists, and a count is + tallied of the number of elements that produce a true value. This count + is returned. count is "iterative" in that it is guaranteed + to apply pred to the list elements in a + left-to-right order. + The counting stops when the shortest list expires. +
+(count even? '(3 1 4 1 5 9 2 5 6)) => 3
+(count < '(1 2 4 8) '(2 4 6 8 10 12 14 16)) => 3
+
+ At least one of the argument lists must be finite: +
+(count < '(3 1 4 1) (circular-list 1 10)) => 2
+
+ +
+ + +

Fold, unfold & map

+
+ +
+ +fold kons knil clist1 clist2 ... -> value +
+ The fundamental list iterator. +

+ First, consider the single list-parameter case. If clist1 = (e1 e2 ... en), + then this procedure returns +

+(kons en ... (kons e2 (kons e1 knil)) ... ) +
+ That is, it obeys the (tail) recursion +
+(fold kons knil lis) = (fold kons (kons (car lis) knil) (cdr lis))
+(fold kons knil '()) = knil
+
+ + Examples: +
+(fold + 0 lis)			; Add up the elements of LIS.
+
+(fold cons '() lis)		; Reverse LIS.
+
+(fold cons tail rev-head)	; See APPEND-REVERSE.
+
+;; How many symbols in LIS?
+(fold (lambda (x count) (if (symbol? x) (+ count 1) count))
+      0
+      lis)
+
+;; Length of the longest string in LIS:
+(fold (lambda (s max-len) (max max-len (string-length s)))
+      0
+      lis)
+
+ + If n list arguments are provided, then the kons function must take + n+1 parameters: one element from each list, and the "seed" or fold + state, which is initially knil. The fold operation terminates when + the shortest list runs out of values: +
+(fold cons* '() '(a b c) '(1 2 3 4 5)) => (c 3 b 2 a 1)
+
+ At least one of the list arguments must be finite. + + +
+ +fold-right kons knil clist1 clist2 ... -> value +
+ The fundamental list recursion operator. +

+ First, consider the single list-parameter case. If clist1 = (e1 e2 ... en), + then this procedure returns +

+(kons e1 (kons e2 ... (kons en knil))) +
+ That is, it obeys the recursion +
+(fold-right kons knil lis) = (kons (car lis) (fold-right kons knil (cdr lis)))
+(fold-right kons knil '()) = knil
+
+ + Examples: +
+(fold-right cons '() lis)		; Copy LIS.
+
+;; Filter the even numbers out of LIS.
+(fold-right (lambda (x l) (if (even? x) (cons x l) l)) '() lis))
+
+ + If n list arguments are provided, then the kons function must take + n+1 parameters: one element from each list, and the "seed" or fold + state, which is initially knil. The fold operation terminates when + the shortest list runs out of values: +
+(fold-right cons* '() '(a b c) '(1 2 3 4 5)) => (a 1 b 2 c 3)
+
+ At least one of the list arguments must be finite. + + +
+ +pair-fold kons knil clist1 clist2 ... -> value +
+ Analogous to fold, but kons is applied to successive sublists of the + lists, rather than successive elements -- that is, kons is applied to the + pairs making up the lists, giving this (tail) recursion: +
+(pair-fold kons knil lis) = (let ((tail (cdr lis)))
+                              (pair-fold kons (kons lis knil) tail))
+(pair-fold kons knil '()) = knil
+
+ For finite lists, the kons function may reliably apply + set-cdr! to the pairs it is given + without altering the sequence of execution. +

+ Example: +

+;;; Destructively reverse a list.
+(pair-fold (lambda (pair tail) (set-cdr! pair tail) pair) '() lis))
+
+ + At least one of the list arguments must be finite. + + + +
+ +pair-fold-right kons knil clist1 clist2 ... -> value +
+ Holds the same relationship with fold-right that pair-fold holds with fold. + Obeys the recursion +
+(pair-fold-right kons knil lis) = 
+    (kons lis (pair-fold-right kons knil (cdr lis)))
+(pair-fold-right kons knil '()) = knil
+
+ + Example: +
+(pair-fold-right cons '() '(a b c)) => ((a b c) (b c) (c))
+
+ + At least one of the list arguments must be finite. + + +
+ +reduce f ridentity list -> value +
+ reduce is a variant of fold. +

+ ridentity should be a "right identity" of the procedure f -- that is, + for any value x acceptable to f, +

+(f x ridentity) = x
+
+ + reduce has the following definition: +
+If list = (), return ridentity;
+Otherwise, return (fold f (car list) (cdr list)). +
+ ...in other words, we compute + (fold f ridentity list). +

+ Note that ridentity is used only in the empty-list case. + You typically use reduce when applying f is expensive and you'd + like to avoid the extra application incurred when fold applies + f to the head of list and the identity value, + redundantly producing the same value passed in to f. + For example, if f involves searching a file directory or + performing a database query, this can be significant. + In general, however, fold is useful in many contexts where reduce is not + (consider the examples given in the fold definition -- only one of the + five folds uses a function with a right identity. + The other four may not be performed with reduce). + +

+ Note: MIT Scheme and Haskell flip F's arg order for their reduce and + fold functions. + +

+;; Take the max of a list of non-negative integers.
+(reduce max 0 nums) ; i.e., (apply max 0 nums)
+
+ + +
+ +reduce-right f ridentity list -> value +
+ reduce-right is the fold-right variant of reduce. + It obeys the following definition: +
+(reduce-right f ridentity '()) = ridentity
+(reduce-right f ridentity '(e1)) = (f e1 ridentity) = e1
+(reduce-right f ridentity '(e1 e2 ...)) =
+    (f e1 (reduce f ridentity (e2 ...)))
+
+ ...in other words, we compute + (fold-right f ridentity list). + +
+;; Append a bunch of lists together.
+;; I.e., (apply append list-of-lists)
+(reduce-right append '() list-of-lists)
+
+ + +
+ +unfold p f g seed [tail-gen] -> list +
+unfold is best described by its basic recursion: +
+(unfold p f g seed) = 
+    (if (p seed) (tail-gen seed)
+        (cons (f seed)
+              (unfold p f g (g seed))))
+
+
+
p
Determines when to stop unfolding. +
f
Maps each seed value to the corresponding list element. +
g
Maps each seed value to next seed value. +
seed
The "state" value for the unfold. +
tail-gen
Creates the tail of the list; + defaults to (lambda (x) '()) +
+

+ In other words, we use g to generate a sequence of seed values +

+seed, g(seed), g2(seed), g3(seed), ... +
+ These seed values are mapped to list elements by f, + producing the elements of the result list in a left-to-right order. + P says when to stop. + +

+ unfold is the fundamental recursive list constructor, + just as fold-right is + the fundamental recursive list consumer. + While unfold may seem a bit abstract + to novice functional programmers, it can be used in a number of ways: + +

+;; List of squares: 1^2 ... 10^2
+(unfold (lambda (x) (> x 10))
+        (lambda (x) (* x x))
+	(lambda (x) (+ x 1))
+	1)
+		
+(unfold null-list? car cdr lis) ; Copy a proper list.
+
+;; Read current input port into a list of values.
+(unfold eof-object? values (lambda (x) (read)) (read))
+
+;; Copy a possibly non-proper list:
+(unfold not-pair? car cdr lis 
+              values)
+
+;; Append HEAD onto TAIL:
+(unfold null-list? car cdr head 
+              (lambda (x) tail))
+
+ + Interested functional programmers may enjoy noting that + fold-right and unfold + are in some sense inverses. + That is, given operations knull?, kar, + kdr, kons, and knil satisfying +
+(kons (kar x) (kdr x)) = x + and +(knull? knil) = #t +
+ then +
+(fold-right kons knil (unfold knull? kar kdr x)) = x +
+ and +
+(unfold knull? kar kdr (fold-right kons knil x)) = x. +
+ + This combinator sometimes is called an "anamorphism;" when an + explicit tail-gen procedure is supplied, it is called an + "apomorphism." + + + +
+ +unfold-right p f g seed [tail] -> list +
+ unfold-right constructs a list with the following loop: +
+(let lp ((seed seed) (lis tail))
+  (if (p seed) lis
+      (lp (g seed)
+          (cons (f seed) lis))))
+
+
+
p
Determines when to stop unfolding. +
f
Maps each seed value to the corresponding list element. +
g
Maps each seed value to next seed value. +
seed
The "state" value for the unfold. +
tail
list terminator; defaults to '(). +
+

+ In other words, we use g to generate a sequence of seed values +

+seed, g(seed), g2(seed), g3(seed), ... +
+ These seed values are mapped to list elements by f, + producing the elements of the result list in a right-to-left order. + P says when to stop. + +

+ unfold-right is the fundamental iterative list constructor, + just as fold is the + fundamental iterative list consumer. + While unfold-right may seem a bit abstract + to novice functional programmers, it can be used in a number of ways: +

+;; List of squares: 1^2 ... 10^2
+(unfold-right zero? 
+              (lambda (x) (* x x))
+              (lambda (x) (- x 1))
+              10)
+	
+;; Reverse a proper list.
+(unfold-right null-list? car cdr lis)
+
+;; Read current input port into a list of values.
+(unfold-right eof-object? values (lambda (x) (read)) (read))
+
+;; (append-reverse rev-head tail)
+(unfold-right null-list? car cdr rev-head tail)
+
+ + Interested functional programmers may enjoy noting that + fold and unfold-right + are in some sense inverses. + That is, given operations knull?, kar, + kdr, kons, and knil satisfying +
+(kons (kar x) (kdr x)) = x + and +(knull? knil) = #t +
+ then +
+(fold kons knil (unfold-right knull? kar kdr x)) = x +
+ and +
+(unfold-right knull? kar kdr (fold kons knil x)) = x. +
+ + This combinator presumably has some pretentious mathematical name; + interested readers are invited to communicate it to the author. + + +
+ +map proc clist1 clist2 ... -> list +
+ [R5RS+] + + proc is a procedure taking as many arguments + as there are list arguments and returning a single value. + map applies proc element-wise to the elements + of the lists and returns a list of the results, + in order. + The dynamic order in which proc + is applied to the elements of the lists is unspecified. + +
+(map cadr '((a b) (d e) (g h))) =>  (b e h)
+
+(map (lambda (n) (expt n n))
+     '(1 2 3 4 5))
+    =>  (1 4 27 256 3125)
+
+(map + '(1 2 3) '(4 5 6)) =>  (5 7 9)
+
+(let ((count 0))
+  (map (lambda (ignored)
+         (set! count (+ count 1))
+         count)
+       '(a b))) =>  (1 2) or (2 1)
+
+ + This procedure is extended from its + R5RS + specification to allow the arguments to be of unequal length; + it terminates when the shortest list runs out. +

+ At least one of the argument lists must be finite: +

+(map + '(3 1 4 1) (circular-list 1 0)) => (4 1 5 1)
+
+ + +
+ +for-each proc clist1 clist2 ... -> unspecified +
+ [R5RS+] + + The arguments to for-each are like the arguments to + map, but + for-each calls proc for its side effects rather + than for its values. + Unlike map, for-each is guaranteed to call + proc on the elements of the lists in order from the first + element(s) to the last, + and the value returned by for-each is unspecified. +
+(let ((v (make-vector 5)))
+  (for-each (lambda (i)
+              (vector-set! v i (* i i)))
+            '(0 1 2 3 4))
+  v)  =>  #(0 1 4 9 16)
+
+ + This procedure is extended from its + R5RS + specification to allow the arguments to be of unequal length; + it terminates when the shortest list runs out. +

+ At least one of the argument lists must be finite. + + +

+ +append-map  f clist1 clist2 ... -> value +
+ +append-map! f clist1 clist2 ... -> value +
+ Equivalent to +
+(apply append (map f clist1 clist2 ...)) +
+ and +
+(apply append! (map f clist1 clist2 ...)) +
+ + Map f over the elements of the lists, just as in the map function. + However, the results of the applications are appended together to + make the final result. append-map uses append to append the results + together; append-map! uses append!. +

+ The dynamic order in which the various applications of f are made is + not specified. +

+ Example: +

+(append-map! (lambda (x) (list x (- x))) '(1 3 8))
+    => (1 -1 3 -3 8 -8)
+
+ + At least one of the list arguments must be finite. + + +
+ +map! f list1 clist2 ... -> list +
+ Linear-update variant of map -- map! is allowed, but not required, to + alter the cons cells of list1 to construct the result list. +

+ The dynamic order in which the various applications of f are made is + not specified. + + In the n-ary case, clist2, clist3, ... must have at least as many + elements as list1. + + +

+ +map-in-order f clist1 clist2 ... -> list +
+ A variant of the map procedure that guarantees to apply f across + the elements of the listi arguments in a left-to-right order. This + is useful for mapping procedures that both have side effects and + return useful values. +

+ At least one of the list arguments must be finite. + + +

+ +pair-for-each f clist1 clist2 ... -> unspecific +
+ Like for-each, but f is applied to successive sublists of the argument + lists. That is, f is applied to the cons cells of the lists, rather + than the lists' elements. These applications occur in left-to-right + order. +

+ The f procedure may reliably apply set-cdr! to the pairs it is given + without altering the sequence of execution. + +

+(pair-for-each (lambda (pair) (display pair) (newline)) '(a b c)) ==>
+    (a b c)
+    (b c)
+    (c)
+
+ + At least one of the list arguments must be finite. + + +
+ +filter-map f clist1 clist2 ... -> list +
+ Like map, but only true values are saved. +
+(filter-map (lambda (x) (and (number? x) (* x x))) '(a 1 b 3 c 7))
+    => (1 9 49)
+
+ The dynamic order in which the various applications of f are made is + not specified. +

+ At least one of the list arguments must be finite. +

+ + +

Filtering & partitioning

+
+ + +
+ +filter pred list -> list +
+ Return all the elements of list that satisfy predicate pred. + The list is not disordered -- elements that appear in the result list + occur in the same order as they occur in the argument list. + The returned list may share a common tail with the argument list. + The dynamic order in which the various applications of pred are made is + not specified. + +
+(filter even? '(0 7 8 8 43 -4)) => (0 8 8 -4)
+
+ + +
+ +partition pred list -> [list list] +
+ Partitions the elements of list with predicate pred, and returns two + values: the list of in-elements and the list of out-elements. + The list is not disordered -- elements occur in the result lists + in the same order as they occur in the argument list. + The dynamic order in which the various applications of pred are made is + not specified. One of the returned lists may share a common tail with the + argument list. + +
+(partition symbol? '(one 2 3 four five 6)) => 
+    (one four five)
+    (2 3 6)
+
+ + +
+ +remove pred list -> list +
+ Returns list without the elements that satisfy predicate pred: +
+(lambda (pred list) (filter (lambda (x) (not (pred x))) list))
+
+ The list is not disordered -- elements that appear in the result list + occur in the same order as they occur in the argument list. + The returned list may share a common tail with the argument list. + The dynamic order in which the various applications of pred are made is + not specified. + +
+(remove even? '(0 7 8 8 43 -4)) => (7 43)
+
+ + +
+ +filter!    pred list -> list +
+ +partition! pred list -> [list list] +
+ +remove!    pred list -> list +
+ Linear-update variants of filter, partition and remove. + These procedures are allowed, but not required, to alter the cons cells + in the argument list to construct the result lists. + +
+ + +

Searching

+

+ +The following procedures all search lists for a leftmost element satisfying +some criteria. This means they do not always examine the entire list; thus, +there is no efficient way for them to reliably detect and signal an error when +passed a dotted or circular list. Here are the general rules describing how +these procedures work when applied to different kinds of lists: + +

+
Proper lists: +
The standard, canonical behavior happens in this case. + +
Dotted lists: +
It is an error to pass these procedures a dotted list + that does not contain an element satisfying the search + criteria. That is, it is an error if the procedure has + to search all the way to the end of the dotted list. + However, this SRFI does not specify anything at all + about the behavior of these procedures when passed a + dotted list containing an element satisfying the search + criteria. It may finish successfully, signal an error, + or perform some third action. Different implementations + may provide different functionality in this case; code + which is compliant with this SRFI may not rely on any + particular behavior. Future SRFI's may refine SRFI-1 + to define specific behavior in this case. +

+ In brief, SRFI-1 compliant code may not pass a dotted + list argument to these procedures. + +

Circular lists: +
It is an error to pass these procedures a circular list + that does not contain an element satisfying the search + criteria. Note that the procedure is not required to + detect this case; it may simply diverge. It is, however, + acceptable to search a circular list if the search is + successful -- that is, if the list contains an element + satisfying the search criteria. +
+

+Here are some examples, using the find and any procedures as canonical +representatives: +

+;; Proper list -- success
+(find even? '(1 2 3))	=> 2
+(any  even? '(1 2 3))	=> #t
+
+;; proper list -- failure
+(find even? '(1 7 3))	=> #f
+(any  even? '(1 7 3))	=> #f
+
+;; Failure is error on a dotted list.
+(find even? '(1 3 . x))	=> error
+(any  even? '(1 3 . x))	=> error
+
+;; The dotted list contains an element satisfying the search.
+;; This case is not specified -- it could be success, an error, 
+;; or some third possibility.
+(find even? '(1 2 . x))	=> error/undefined
+(any  even? '(1 2 . x))	=> error/undefined ; success, error or other.
+
+;; circular list -- success
+(find even? (circular-list 1 6 3)) => 6
+(any  even? (circular-list 1 6 3)) => #t
+
+;; circular list -- failure is error. Procedure may diverge.
+(find even? (circular-list 1 3)) => error
+(any  even? (circular-list 1 3)) => error
+
+ +
+ +
+ +find pred clist -> value +
+ Return the first element of clist that satisfies predicate pred; + false if no element does. + +
+(find even? '(3 1 4 1 5 9)) => 4
+
+ + Note that find has an ambiguity in its lookup semantics -- if find + returns #f, you cannot tell (in general) if it found a #f element + that satisfied pred, or if it did not find any element at all. In + many situations, this ambiguity cannot arise -- either the list being + searched is known not to contain any #f elements, or the list is + guaranteed to have an element satisfying pred. However, in cases + where this ambiguity can arise, you should use find-tail instead of + find -- find-tail has no such ambiguity: +
+(cond ((find-tail pred lis) => (lambda (pair) ...)) ; Handle (CAR PAIR)
+      (else ...)) ; Search failed.
+
+ + +
+ +find-tail pred clist -> pair or false +
+ Return the first pair of clist whose car satisfies pred. If no pair does, + return false. +

+ find-tail can be viewed as a general-predicate variant of the member + function. +

+ Examples: +

+(find-tail even? '(3 1 37 -8 -5 0 0)) => (-8 -5 0 0)
+(find-tail even? '(3 1 37 -5)) => #f
+
+;; MEMBER X LIS:
+(find-tail (lambda (elt) (equal? x elt)) lis)
+
+ + In the circular-list case, this procedure "rotates" the list. + +

+ Find-tail is essentially drop-while, + where the sense of the predicate is inverted: + Find-tail searches until it finds an element satisfying + the predicate; drop-while searches until it finds an + element that doesn't satisfy the predicate. + + +

+ +take-while  pred clist -> list +
+ +take-while! pred clist -> list +
+ +Returns the longest initial prefix of clist whose elements all +satisfy the predicate pred. + +

+Take-while! is the linear-update variant. It is allowed, but not +required, to alter the argument list to produce the result. + +

+(take-while even? '(2 18 3 10 22 9)) => (2 18)
+
+ + +
+ +drop-while pred clist -> list +
+Drops the longest initial prefix of clist whose elements all +satisfy the predicate pred, and returns the rest of the list. + +
+(drop-while even? '(2 18 3 10 22 9)) => (3 10 22 9)
+
+The circular-list case may be viewed as "rotating" the list. + + + +
+ +span   pred clist -> [list clist] +
+ +span!  pred list  -> [list list] +
+ +break  pred clist -> [list clist] +
+ +break! pred list  -> [list list] +
+ +Span splits the list into the longest initial prefix whose +elements all satisfy pred, and the remaining tail. +Break inverts the sense of the predicate: +the tail commences with the first element of the input list +that satisfies the predicate. + +

+In other words: +span finds the intial span of elements +satisfying pred, +and break breaks the list at the first element satisfying +pred. + +

+Span is equivalent to +

+(values (take-while pred clist) 
+        (drop-while pred clist))
+
+ +

+Span! and break! are the linear-update variants. +They are allowed, but not required, +to alter the argument list to produce the result. + +

+(span even? '(2 18 3 10 22 9)) =>
+  (2 18)
+  (3 10 22 9)
+
+(break even? '(3 1 4 1 5 9)) =>
+  (3 1)
+  (4 1 5 9)
+
+ + + +
+ +any pred clist1 clist2 ... -> value +
+ Applies the predicate across the lists, returning true if the predicate + returns true on any application. +

+ If there are n list arguments clist1 ... clistn, then pred must be a + procedure taking n arguments and returning a boolean result. +

+ any applies pred to the first elements of the clisti parameters. + If this application returns a true value, any immediately returns + that value. Otherwise, it iterates, applying pred to the second + elements of the clisti parameters, then the third, and so forth. + The iteration stops when a true value is produced or one of the lists runs + out of values; in + the latter case, any returns #f. + The application of pred to the last element of the + lists is a tail call. +

+ Note the difference between find and any -- find returns the element + that satisfied the predicate; any returns the true value that the + predicate produced. +

+ Like every, any's name does not end with a question mark -- this is to + indicate that it does not return a simple boolean (#t or #f), but a + general value. + +

+(any integer? '(a 3 b 2.7))   => #t
+(any integer? '(a 3.1 b 2.7)) => #f
+(any < '(3 1 4 1 5)
+       '(2 7 1 8 2)) => #t
+
+ + +
+ +every pred clist1 clist2 ... -> value +
+ Applies the predicate across the lists, returning true if the predicate + returns true on every application. +

+ If there are n list arguments clist1 ... clistn, then pred must be a + procedure taking n arguments and returning a boolean result. +

+ every applies pred to the first elements of the clisti parameters. + If this application returns false, every immediately returns false. + Otherwise, it iterates, applying pred to the second elements of the + clisti parameters, then the third, and so forth. The iteration stops + when a false value is produced or one of the lists runs out of values. + In the latter case, every returns + the true value produced by its final application of pred. + The application of pred to the last element of the lists + is a tail call. +

+ If one of the clisti has no elements, every simply returns #t. +

+ Like any, every's name does not end with a question mark -- this is to + indicate that it does not return a simple boolean (#t or #f), but a + general value. + + +

+ +list-index pred clist1 clist2 ... -> integer or false +
+ Return the index of the leftmost element that satisfies pred. +

+ If there are n list arguments clist1 ... clistn, then pred must be a + function taking n arguments and returning a boolean result. +

+ list-index applies pred to the first elements of the clisti parameters. + If this application returns true, list-index immediately returns zero. + Otherwise, it iterates, applying pred to the second elements of the + clisti parameters, then the third, and so forth. When it finds a tuple of + list elements that cause pred to return true, it stops and returns the + zero-based index of that position in the lists. +

+ The iteration stops when one of the lists runs out of values; in this + case, list-index returns #f. + +

+(list-index even? '(3 1 4 1 5 9)) => 2
+(list-index < '(3 1 4 1 5 9 2 5 6) '(2 7 1 8 2)) => 1
+(list-index = '(3 1 4 1 5 9 2 5 6) '(2 7 1 8 2)) => #f
+
+ + +
+ +member x list [=] -> list +
+ +memq x list -> list +
+ +memv x list -> list +
+ [R5RS+] + + These procedures return the first sublist of list whose car is + x, where the sublists of list are the + non-empty lists returned by + (drop list i) + for i less than the length of list. + If x does + not occur in list, then #f is returned. + memq uses eq? to compare x + with the elements of list, + while memv uses eqv?, and + member uses equal?. + +
+    (memq 'a '(a b c))          =>  (a b c)
+    (memq 'b '(a b c))          =>  (b c)
+    (memq 'a '(b c d))          =>  #f
+    (memq (list 'a) '(b (a) c)) =>  #f
+    (member (list 'a)
+            '(b (a) c))         =>  ((a) c)
+    (memq 101 '(100 101 102))   =>  *unspecified*
+    (memv 101 '(100 101 102))   =>  (101 102)
+
+ + member is extended from its + R5RS + definition to allow the client to pass in + an optional equality procedure = used to compare keys. + +

+ The comparison procedure is used to compare the elements ei of list + to the key x in this way: +

+(= x ei) ; list is (E1 ... En) +
+ That is, the first argument is always x, and the second argument is + one of the list elements. Thus one can reliably find the first element + of list that is greater than five with + (member 5 list <) + +

+ Note that fully general list searching may be performed with + the find-tail and find procedures, e.g. +

+(find-tail even? list) ; Find the first elt with an even key.
+
+ +
+ + +

Deletion

+

+ +

+ +
+ +delete  x list [=] -> list +
+ +delete! x list [=] -> list +
+ delete uses the comparison procedure =, which defaults to equal?, to find + all elements of list that are equal to x, and deletes them from list. The + dynamic order in which the various applications of = are made is not + specified. + +

+ The list is not disordered -- elements that appear in the result list + occur in the same order as they occur in the argument list. + The result may share a common tail with the argument list. + +

+ Note that fully general element deletion can be performed with the remove + and remove! procedures, e.g.: +

+;; Delete all the even elements from LIS:
+(remove even? lis)
+
+ + The comparison procedure is used in this way: + (= x ei). + That is, x is always the first argument, + and a list element is always the + second argument. The comparison procedure will be used to compare each + element of list exactly once; the order in which it is applied to the + various ei is not specified. Thus, one can reliably remove all the + numbers greater than five from a list with + (delete 5 list <) + +

+ delete! is the linear-update variant of delete. + It is allowed, but not required, to alter the cons cells in + its argument list to construct the result. + + +

+ +delete-duplicates  list [=] -> list +
+ +delete-duplicates! list [=] -> list +
+ delete-duplicates removes duplicate elements from the + list argument. + If there are multiple equal elements in the argument list, the result list + only contains the first or leftmost of these elements in the result. + The order of these surviving elements is the same as in the original + list -- delete-duplicates does not disorder the list (hence it is useful + for "cleaning up" association lists). +

+ The = parameter is used to compare the elements of the list; it defaults + to equal?. If x comes before y in list, then the comparison is performed + (= x y). + The comparison procedure will be used to compare each pair of elements in + list no more than once; + the order in which it is applied to the various pairs is not specified. +

+ Implementations of delete-duplicates + are allowed to share common tails + between argument and result lists -- for example, if the list argument + contains only unique elements, it may simply return exactly + this list. +

+ Be aware that, in general, delete-duplicates + runs in time O(n2) for n-element lists. + Uniquifying long lists can be accomplished in O(n lg n) time by sorting + the list to bring equal elements together, then using a linear-time + algorithm to remove equal elements. Alternatively, one can use algorithms + based on element-marking, with linear-time results. + +

+ delete-duplicates! is the linear-update variant of delete-duplicates; it + is allowed, but not required, to alter the cons cells in its argument + list to construct the result. +

+(delete-duplicates '(a b a c a b c z)) => (a b c z)
+
+;; Clean up an alist:
+(delete-duplicates '((a . 3) (b . 7) (a . 9) (c . 1))
+                   (lambda (x y) (eq? (car x) (car y))))
+    => ((a . 3) (b . 7) (c . 1))
+
+
+ + +

Association lists

+

+An "association list" (or "alist") is a list of pairs. The car of each pair +contains a key value, and the cdr contains the associated data value. They can +be used to construct simple look-up tables in Scheme. Note that association +lists are probably inappropriate for performance-critical use on large data; +in these cases, hash tables or some other alternative should be employed. + +

+ +
+ +assoc key alist [=] -> pair or #f +
+ +assq key alist -> pair or #f +
+ +assv key alist -> pair or #f +
+ + [R5RS+] + alist must be an association list -- a list of pairs. + These procedures + find the first pair in alist whose car field is key, + and returns that pair. + If no pair in alist has key as its car, + then #f is returned. + assq uses eq? to compare key + with the car fields of the pairs in alist, + while assv uses eqv? + and assoc uses equal?. +
+(define e '((a 1) (b 2) (c 3)))
+(assq 'a e)                            =>  (a 1)
+(assq 'b e)                            =>  (b 2)
+(assq 'd e)                            =>  #f
+(assq (list 'a) '(((a)) ((b)) ((c))))  =>  #f
+(assoc (list 'a) '(((a)) ((b)) ((c)))) =>  ((a))
+(assq 5 '((2 3) (5 7) (11 13)))	   =>  *unspecified*
+(assv 5 '((2 3) (5 7) (11 13)))	   =>  (5 7)
+
+ + assoc is extended from its + R5RS + definition to allow the client to pass in + an optional equality procedure = used to compare keys. + +

+ The comparison procedure is used to compare the elements ei of list + to the key parameter in this way: +

+(= key (car ei)) ; list is (E1 ... En) +
+ That is, the first argument is always key, + and the second argument is one of the list elements. + Thus one can reliably find the first entry + of alist whose key is greater than five with + (assoc 5 alist <) + +

+ Note that fully general alist searching may be performed with + the find-tail and find procedures, e.g. +

+;; Look up the first association in alist with an even key:
+(find (lambda (a) (even? (car a))) alist)
+
+ + + +
+ +alist-cons key datum alist -> alist +
+
+(lambda (key datum alist) (cons (cons key datum) alist))
+
+ Cons a new alist entry mapping key -> datum onto alist. + + +
+ +alist-copy alist -> alist +
+ Make a fresh copy of alist. This means copying each pair that + forms an association as well as the spine of the list, i.e. +
+(lambda (a) (map (lambda (elt) (cons (car elt) (cdr elt))) a))
+
+ + +
+ +alist-delete  key alist [=] -> alist +
+ +alist-delete! key alist [=] -> alist +
+ alist-delete deletes all associations from alist with the given key, + using key-comparison procedure =, which defaults to equal?. + The dynamic order in which the various applications of = are made is not + specified. +

+ Return values may share common tails with the alist argument. + The alist is not disordered -- elements that appear in the result alist + occur in the same order as they occur in the argument alist. +

+ The comparison procedure is used to compare the element keys ki of alist's + entries to the key parameter in this way: + (= key ki). + Thus, one can reliably remove all entries of alist whose key is greater + than five with + (alist-delete 5 alist <) +

+ alist-delete! is the linear-update variant of alist-delete. + It is allowed, but not required, + to alter cons cells from the alist parameter to construct the result. + +

+ + + +

Set operations on lists

+

+These procedures implement operations on sets represented as lists of elements. +They all take an = argument used to compare elements of lists. +This equality procedure is required to be consistent with eq?. +That is, it must be the case that +

+ (eq? x y) => (= x y). +
+Note that this implies, in turn, that two lists that are eq? are +also set-equal by any legal comparison procedure. This allows for +constant-time determination of set operations on eq? lists. + +

+Be aware that these procedures typically run in time +O(n * m) +for n- and m-element list arguments. +Performance-critical applications +operating upon large sets will probably wish to use other data +structures and algorithms. + +

+ +
+ +lset<= = list1 ... -> boolean +
+ Returns true iff every listi is a subset of listi+1, using = for + the element-equality procedure. + List A is a subset of list B if every + element in A is equal to some element of B. + When performing an element comparison, + the = procedure's first argument is an element + of A; its second, an element of B. +
+(lset<= eq? '(a) '(a b a) '(a b c c)) => #t
+
+(lset<= eq?) => #t             ; Trivial cases
+(lset<= eq? '(a)) => #t
+
+ + +
+ +lset= = list1 list2 ... -> boolean +
+ Returns true iff every listi is set-equal to listi+1, using = for + the element-equality procedure. "Set-equal" simply means that + listi is a subset of listi+1, and listi+1 is a subset of listi. + The = procedure's first argument is an element of listi; its second is an element of + listi+1. +
+(lset= eq? '(b e a) '(a e b) '(e e b a)) => #t
+
+(lset= eq?) => #t               ; Trivial cases
+(lset= eq? '(a)) => #t
+
+ + +
+ +lset-adjoin = list elt1 ... -> list +
+ Adds the elti elements not already in the list parameter to the + result list. The result shares a common tail with the list parameter. + The new elements are added to the front of the list, but no guarantees + are made about their order. The = parameter is an equality procedure + used to determine if an elti is already a member of list. Its first + argument is an element of list; its second is one of the elti. +

+ The list parameter is always a suffix of the result -- even if the list + parameter contains repeated elements, these are not reduced. +

+(lset-adjoin eq? '(a b c d c e) 'a 'e 'i 'o 'u) => (u o i a b c d c e)
+
+ + +
+ +lset-union = list1 ... -> list +
+ Returns the union of the lists, using = for the element-equality + procedure. +

+ The union of lists A and B is constructed as follows: +

+ However, there is no guarantee that = will be applied to every pair + of arguments from A and B. + In particular, if A is eq? to B, + the operation may immediately terminate. + +

+ In the n-ary case, the two-argument list-union operation is simply + folded across the argument lists. + +

+(lset-union eq? '(a b c d e) '(a e i o u)) => 
+    (u o i a b c d e)
+
+;; Repeated elements in LIST1 are preserved.
+(lset-union eq? '(a a c) '(x a x)) => (x a a c)
+
+;; Trivial cases
+(lset-union eq?) => ()
+(lset-union eq? '(a b c)) => (a b c)
+
+ + +
+ +lset-intersection = list1 list2 ... -> list +
+ Returns the intersection of the lists, + using = for the element-equality procedure. +

+ The intersection of lists A and B + is comprised of every element of A that is = + to some element of B: + (= a b), + for a in A, and b in B. + Note this implies that an element which appears in B + and multiple times in list A + will also appear multiple times in the result. +

+ The order in which elements appear in the result is the same as + they appear in list1 -- + that is, lset-intersection essentially filters + list1, + without disarranging element order. + The result may + share a common tail with list1. +

+ In the n-ary case, the two-argument list-intersection operation is simply + folded across the argument lists. However, the dynamic order in which the + applications of = are made is not specified. + The procedure may check an + element of list1 for membership + in every other list before proceeding to + consider the next element of list1, + or it may completely intersect list1 + and list2 + before proceeding to list3, + or it may go about its work in some third order. + +

+(lset-intersection eq? '(a b c d e) '(a e i o u)) => (a e)
+
+;; Repeated elements in LIST1 are preserved.
+(lset-intersection eq? '(a x y a) '(x a x z)) => '(a x a)
+
+(lset-intersection eq? '(a b c)) => (a b c)     ; Trivial case
+
+ + +
+ +lset-difference = list1 list2 ... -> list +
+ Returns the difference of the lists, using = for the element-equality + procedure -- all the elements of list1 that are not + = to any element from one of the + other listi parameters. +

+ The = procedure's first argument is + always an element of list1; + its second is an element of one of the other listi. + Elements that are repeated multiple times in the + list1 parameter + will occur multiple times in the result. + + The order in which elements appear in the result is the same as + they appear in list1 -- + that is, lset-difference essentially + filters list1, without disarranging element order. + The result may share a common tail with list1. + + The dynamic order in which the applications of = are made is not + specified. + The procedure may check an element of list1 + for membership in every other list before proceeding to consider the + next element of list1, + or it may completely compute the difference of + list1 and list2 before + proceeding to list3, + or it may go about its work in some third order. + +

+(lset-difference eq? '(a b c d e) '(a e i o u)) => (b c d)
+
+(lset-difference eq? '(a b c)) => (a b c) ; Trivial case
+
+ + +
+ +lset-xor = list1 ... -> list +
+ Returns the exclusive-or of the sets, + using = for the element-equality procedure. + If there are exactly two lists, this is all the elements + that appear in exactly one of the two lists. The operation is associative, + and thus extends to the n-ary case -- the elements that appear in an + odd number of the lists. The result may share a common tail with any of + the listi parameters. +

+ More precisely, for two lists A and B, + A xor B is a list of +

+ However, an implementation is allowed to assume that = is + symmetric -- that is, that +
+ (= a b) => + (= b a). +
+ This means, for example, that if a comparison + (= a b) produces + true for some a in A + and b in B, + both a and b may be removed from + inclusion in the result. +

+ In the n-ary case, the binary-xor operation is simply folded across + the lists. + +

+(lset-xor eq? '(a b c d e) '(a e i o u)) => (d c b i o u)
+
+;; Trivial cases.
+(lset-xor eq?) => ()
+(lset-xor eq? '(a b c d e)) => (a b c d e)
+
+ + + +
+ +lset-diff+intersection = list1 list2 ... -> [list list] +
+ Returns two values -- the difference and the intersection of the lists. + Is equivalent to +
+(values (lset-difference = list1 list2 ...)
+        (lset-intersection = list1
+                             (lset-union = list2 ...)))
+
+ but can be implemented more efficiently. +

+ The = procedure's first argument is an element of list1; its second + is an element of one of the other listi. +

+ Either of the answer lists may share a + common tail with list1. + This operation essentially partitions list1. + + +

+ +lset-union!             = list1 ... -> list +
+ +lset-intersection!      = list1 list2 ... -> list +
+ +lset-difference!        = list1 list2 ... -> list +
+ +lset-xor!               = list1 ... -> list +
+ +lset-diff+intersection! = list1 list2 ... -> [list list] +
+ These are linear-update variants. They are allowed, but not required, + to use the cons cells in their first list parameter to construct their + answer. lset-union! is permitted to recycle cons cells from any + of its list arguments. +
+ + +

Primitive side-effects

+

+These two procedures are the primitive, +R5RS +side-effect operations on pairs. + +

+ +
+ +set-car! pair object -> unspecified +
+ +set-cdr! pair object -> unspecified +
+ [R5RS] + These procedures store object in the car and cdr field + of pair, respectively. + The value returned is unspecified. +
+(define (f) (list 'not-a-constant-list))
+(define (g) '(constant-list))
+(set-car! (f) 3) =>  *unspecified*
+(set-car! (g) 3) =>  *error*
+
+
+ + +

Acknowledgements

+

+The design of this library benefited greatly from the feedback provided during +the SRFI discussion phase. Among those contributing thoughtful commentary and +suggestions, both on the mailing list and by private discussion, were Mike +Ashley, Darius Bacon, Alan Bawden, Phil Bewig, Jim Blandy, Dan Bornstein, Per +Bothner, Anthony Carrico, Doug Currie, Kent Dybvig, Sergei Egorov, Doug Evans, +Marc Feeley, Matthias Felleisen, Will Fitzgerald, Matthew Flatt, Dan Friedman, +Lars Thomas Hansen, Brian Harvey, Erik Hilsdale, Wolfgang Hukriede, Richard +Kelsey, Donovan Kolbly, Shriram Krishnamurthi, Dave Mason, Jussi Piitulainen, +David Pokorny, Duncan Smith, Mike Sperber, Maciej Stachowiak, Harvey J. Stein, +John David Stone, and Joerg F. Wittenberger. I am grateful to them for their +assistance. +

+I am also grateful the authors, implementors and documentors of all the systems +mentioned in the rationale. Aubrey Jaffer and Kent Pitman should be noted +for their work in producing Web-accessible versions of the R5RS and +Common Lisp spec, which was a tremendous aid. +

+This is not to imply that these individuals necessarily endorse the final +results, of course. + + + +

References & links

+

+ +

+
This document, in HTML: +
+ http://srfi.schemers.org/srfi-1/srfi-1.html + +
Source code for the reference implementation: +
+ http://srfi.schemers.org/srfi-1/srfi-1-reference.scm + +
Archive of SRFI-1 discussion-list email: +
+ http://srfi.schemers.org/srfi-1/mail-archive/maillist.html + +
SRFI web site: +
+ http://srfi.schemers.org/ +
+ +

+ +

+
[CommonLisp]
+
Common Lisp: the Language
+Guy L. Steele Jr. (editor).
+Digital Press, Maynard, Mass., second edition 1990.
+Available at +http://www.elwood.com/alu/table/references.htm#cltl2. +

+ +The Common Lisp "HyperSpec," produced by Kent Pitman, is essentially +the ANSI spec for Common Lisp: + +http://www.harlequin.com/education/books/HyperSpec/. + +

[R5RS]
+
Revised5 report on the algorithmic language Scheme.
+ R. Kelsey, W. Clinger, J. Rees (editors).
+ Higher-Order and Symbolic Computation, Vol. 11, No. 1, September, 1998.
+ and ACM SIGPLAN Notices, Vol. 33, No. 9, October, 1998.
+ Available at + http://www.schemers.org/Documents/Standards/. + +
+ + + + +

Copyright

+

+ +Certain portions of this document -- the specific, marked segments of text +describing the R5RS procedures -- were adapted with permission from the R5RS +report. +

+ +All other text is copyright (C) Olin Shivers (1998, 1999). +All Rights Reserved. +

+Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: +

+

+The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. +

+

+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +

+ + diff --git a/doc/srfi-std/srfi-11.html b/doc/srfi-std/srfi-11.html new file mode 100644 index 0000000000..987edd5714 --- /dev/null +++ b/doc/srfi-std/srfi-11.html @@ -0,0 +1,184 @@ + + + + SRFI 11: Syntax for receiving multiple values + + + + +

Title

+ +SRFI 11: Syntax for receiving multiple values + +

Author

+ +Lars T Hansen + +

Status

+This SRFI is currently in ``final'' status. To see an explanation of each status +that a SRFI can hold, see here. +You can access the discussion via the archive of +the mailing list. +

+ +

Abstract

+ +The SRFI introduces syntactic forms LET-VALUES and LET*-VALUES that bind the values of +expressions that return multiple values. + +

Issues

+ +None. + +

Rationale

+ +LET-VALUES and LET*-VALUES reduce the clutter of the CALL-WITH-VALUES notation for +receiving multiple values. + + +

Specification

+ +
+
+(LET-VALUES ((<formals> <expression>) ...) <body>) +
Syntax +
+

Each <formals> should be a formal arguments list as for a LAMBDA + expression, cf section 4.1.4 of the R5RS.

+ +

The <expression>s are evaluated in the current environment, the + variables of the <formals> are bound to fresh locations, the return + values of the <expression>s are stored in the variables, the <body> + is evaluated in the extended environment, and the values of the + last expression of <body> are returned. The <body> is a + <tail body>, cf section 3.5 of the R5RS.

+ +

The matching of each <formals> to values is as for the matching of + <formals> to arguments in a LAMBDA expression, and it is an + error for an <expression> to return a number of values that does + not match its corresponding <formals>.

+ +

+

+             (let-values (((a b . c) (values 1 2 3 4)))
+               (list a b c))            --> (1 2 (3 4))
+
+             (let ((a 'a) (b 'b) (x 'x) (y 'y))
+               (let-values (((a b) (values x y))
+                            ((x y) (values a b)))
+                 (list a b x y)))       --> (x y a b)
+
+

+ +
+(LET*-VALUES ((<formals> <expression>) ...) <body>) +
Syntax +
+

Each <formals> should be a formal arguments list as for a LAMBDA + expression, cf section 4.1.4 of the R5RS.

+ +

LET*-VALUES is similar to LET-VALUES, but the bindings are performed + sequentially from left to right, and the region of a binding indicated + by (<formals> <expression>) is that part of the LET*-VALUES + expression to the right of the binding. Thus the second binding is done in + an environment in which the first binding is visible, and so on.

+ +

+

+             (let ((a 'a) (b 'b) (x 'x) (y 'y))
+               (let*-values (((a b) (values x y))
+                             ((x y) (values a b)))
+                 (list a b x y)))       --> (x y x y)
+
+

+
+ +

Implementation

+ +The following implementation is written in R5RS Scheme. It is not +compatible with the IEEE Scheme standard because the IEEE standard does +not contain the high-level macro system. + +

The implementation assumes that some top-level names defined by the +R5RS are bound to their original values. + +

+

+;; This code is in the public domain.
+
+(define-syntax let-values
+  (syntax-rules ()
+    ((let-values (?binding ...) ?body0 ?body1 ...)
+     (let-values "bind" (?binding ...) () (begin ?body0 ?body1 ...)))
+    
+    ((let-values "bind" () ?tmps ?body)
+     (let ?tmps ?body))
+    
+    ((let-values "bind" ((?b0 ?e0) ?binding ...) ?tmps ?body)
+     (let-values "mktmp" ?b0 ?e0 () (?binding ...) ?tmps ?body))
+    
+    ((let-values "mktmp" () ?e0 ?args ?bindings ?tmps ?body)
+     (call-with-values 
+       (lambda () ?e0)
+       (lambda ?args
+         (let-values "bind" ?bindings ?tmps ?body))))
+    
+    ((let-values "mktmp" (?a . ?b) ?e0 (?arg ...) ?bindings (?tmp ...) ?body)
+     (let-values "mktmp" ?b ?e0 (?arg ... x) ?bindings (?tmp ... (?a x)) ?body))
+    
+    ((let-values "mktmp" ?a ?e0 (?arg ...) ?bindings (?tmp ...) ?body)
+     (call-with-values
+       (lambda () ?e0)
+       (lambda (?arg ... . x)
+         (let-values "bind" ?bindings (?tmp ... (?a x)) ?body))))))
+
+(define-syntax let*-values
+  (syntax-rules ()
+    ((let*-values () ?body0 ?body1 ...)
+     (begin ?body0 ?body1 ...))
+
+    ((let*-values (?binding0 ?binding1 ...) ?body0 ?body1 ...)
+     (let-values (?binding0)
+       (let*-values (?binding1 ...) ?body0 ?body1 ...)))))
+
+ +

Copyright

+

Copyright (C) Lars T Hansen (1999). All Rights Reserved.

+ +

+Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: +

+

+The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. +

+

+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +

+ +
+
Editor: Mike Sperber
+ + +Last modified: Tue Sep 28 10:59:57 MST 2004 + + + diff --git a/doc/srfi-std/srfi-13.html b/doc/srfi-std/srfi-13.html new file mode 100644 index 0000000000..2f744dc119 --- /dev/null +++ b/doc/srfi-std/srfi-13.html @@ -0,0 +1,2972 @@ + + + + + + + + + SRFI 13: String Libraries + + + + + + + + + + +

Title

+ +
SRFI 13: String Libraries
+ + +

Author

+ +Olin Shivers + +

Status

+This SRFI is currently in ``final'' status. To see an explanation of each status that a SRFI can hold, see here. +You can access the discussion via the archive of the mailing list. +

+ +

Table of contents

+ + + + + +

Abstract

+

+ +R5RS +Scheme has an impoverished set of string-processing utilities, which is a +problem for authors of portable code. This SRFI proposes a coherent and comprehensive set of +string-processing procedures; it is accompanied by a reference implementation +of the spec. The reference implementation is +

+

+The routines in this SRFI are backwards-compatible with the string-processing +routines of +R5RS. + + +

Procedure Index

+

+Here is a list of the procedures provided by the string-lib +and string-lib-internals packages. +R5RS +procedures are shown in +bold; +extended R5RS + + procedures, in bold italic. +

+
+
Predicates +
+
+string? string-null? 
+string-every string-any
+
+ +
Constructors +
+
+make-string string string-tabulate
+
+ +
List & string conversion +
+
+string->list list->string
+reverse-list->string string-join
+
+ +
Selection +
+
+string-length
+string-ref
+string-copy
+substring/shared
+string-copy! 
+string-take string-take-right
+string-drop string-drop-right
+string-pad  string-pad-right 
+string-trim string-trim-right string-trim-both 
+
+ +
Modification +
+
+string-set! string-fill!
+
+ +
Comparison +
+
+string-compare string-compare-ci
+string<>     string=    string<    string>    string<=    string>=
+string-ci<>  string-ci= string-ci< string-ci> string-ci<= string-ci>=
+string-hash  string-hash-ci
+
+ +
Prefixes & suffixes +
+
+string-prefix-length    string-suffix-length
+string-prefix-length-ci string-suffix-length-ci
+
+string-prefix?    string-suffix?    
+string-prefix-ci? string-suffix-ci? 
+
+ +
Searching +
+
+string-index string-index-right
+string-skip  string-skip-right 
+string-count 
+string-contains string-contains-ci
+
+ +
Alphabetic case mapping +
+
+string-titlecase  string-upcase  string-downcase
+string-titlecase! string-upcase! string-downcase!
+
+ +
Reverse & append +
+
+string-reverse string-reverse!
+string-append
+string-concatenate
+string-concatenate/shared string-append/shared
+string-concatenate-reverse string-concatenate-reverse/shared
+
+ +
Fold, unfold & map +
+
+string-map      string-map!
+string-fold     string-fold-right
+string-unfold   string-unfold-right
+string-for-each string-for-each-index
+
+ +
Replicate & rotate +
+
+xsubstring string-xcopy!
+
+ +
Miscellaneous: insertion, parsing +
+
+string-replace string-tokenize
+
+ +
Filtering & deleting +
+
+string-filter string-delete 
+
+ +
Low-level procedures +
+
+string-parse-start+end
+string-parse-final-start+end
+let-string-start+end
+
+check-substring-spec
+substring-spec-ok?
+
+make-kmp-restart-vector kmp-step string-kmp-partial-search
+
+ +
+
+ + +

Rationale

+

+ +This SRFI defines two libraries that provide a rich set of operations for +manipulating strings. These are frequently useful for scripting and other +text-manipulation applications. The library's design was influenced by the +string libraries found in MIT Scheme, Gambit, RScheme, MzScheme, slib, Common +Lisp, Bigloo, guile, Chez, APL, Java, and the SML standard basis. +

+ +All procedures involving character comparison are available in +both case-sensitive and case-insensitive forms. +

+ +All functionality is available in substring and full-string forms. + + +

Strings are code-point sequences

+

+This SRFI considers strings simply to be a sequence of "code points" or +character encodings. Operations such as comparison or reversal are always done +code point by code point. See the comments below on super-ASCII character +types for implications that follow. +

+ +It's entirely possible that a legal string might not be a sensible "text" +sequence. For example, consider a string comprised entirely of zero-width +Unicode accent characters with no preceding base character to modify -- +this is a legal string, albeit one that does not make a great deal of sense +when interpreted as a sequence of natural-language text. The routines in +this SRFI do not handle these "text" concerns; they restrict themselves +to the underlying view of strings as merely a sequence of "code points." + + +

String operations are locale- and context-independent

+

+ +This SRFI defines string operations that are locale- and context-independent. +While it is certainly important to have a locale-sensitive comparison or +collation procedure when processing text, it is also important to have a suite +of operations that are reliably invariant for basic string processing --- +otherwise, a change of locale could cause data structures such as hash tables, +b-trees, symbol tables, directories of filenames, etc. +to become corrupted. + +

+Locale- and context-sensitive text operations, such as collation, are +explicitly deferred to a subsequent, companion "text" SRFI. + + +

Internationalisation & super-ASCII character types

+ +

+The major issue confronting this SRFI is the existence of super-ASCII +character encodings, such as eight-bit Latin-1 or 16- and 32-bit Unicode. It +is a design goal of this SRFI for the API to be portable across string +implementations based on at least these three standard encodings. +Unfortunately, this places strong limitations on the API design. Here are +some relevant issues. Be warned that life in a super-ASCII world is +significantly more complex; there are no easy answers for many of these issues. + + +

Case mapping and case-folding

+ +

+Upper- and lower-casing characters is complex in super-ASCII encodings. + +

+ +

+The Unicode Consortium's web site +

+ http://www.unicode.org/ +
+

+has detailed discussions of the issues. See in particular technical report +21 on case mappings +

+ http://www.unicode.org/unicode/reports/tr21/ +
+ +

+SRFI 13 makes no attempt to deal with these issues; it uses a simple 1-1 +locale- and context-independent case-mapping, specifically Unicode's 1-1 +case-mappings given in +

+ ftp://ftp.unicode.org/Public/UNIDATA/UnicodeData.txt +
+

+The format of this file is explained in +

+ ftp://ftp.unicode.org/Public/UNIDATA/UnicodeData.html +
+

+Note that this means that German eszet upper-cases to itself, not "SS". + +

+Case-mapping and case-folding operations in SRFI 13 are locale-independent so +that shifting locales won't wreck hash tables, b-trees, symbol tables, etc. + + + +

String equality & string normalisation

+ +

+Comparing strings for equality is complicated because in some cases Unicode +actually provides multiple encodings for the "same" character, and because +what we usually think of as a "character" can be represented in Unicode as a +sequence of several code-points. For example, consider the letter "e" with +an acute accent. There is a single Unicode character for this. However, +Unicode also allows one to represent this with a two-character sequence: the +"e" character followed by a zero-width acute-accent character. As another +example, Unicode provides some Asian characters in "narrow" and "full" widths. + +

+There are multiple ways we might want to compare strings for equality. In +(roughly) decreasing order of precision, + +

+ +

+This library does not address these complexities. SRFI 13 string equality is +simply based upon comparing the encoding values used for the characters. +Accent-insensitive and other types of comparison are not provided; only +a simple form of case-insensitive comparison is provided, which uses the +1-1 case mappings specified by Unicode in +

+ ftp://ftp.unicode.org/Public/UNIDATA/UnicodeData.txt +
+

+These are adequate for "program" or "systems" use of strings (e.g., to +manipulate program identifiers and operating-system filenames). + + +

String inequality

+ +

+Above and beyond the issues arising in string-equality, when we attempt +to order strings there are even further considerations. + +

+ +

+Unicode defines a complex set of machinery for ordering or "collating" +strings, which involves mapping each string to a multi-byte sort key, +and then doing simple lexicographic sorting with these keys. These rules +can be overlaid by additional domain- or language-specific rules. Again, +this SRFI does not address these issues. SRFI 13 string ordering is strictly +based upon a character-by-character comparison of the values used for +representing the string. + + +

Naming conventions

+ +

+This library contains a large number of procedures, but they follow +a consistent naming scheme, and are consistent with the conventions +developed in SRFI 1. The names are composed of smaller lexemes +in a regular way that exposes the structure and relationships between the +procedures. This should help the programmer to recall or reconstitute the name +of the particular procedure that he needs when writing his own code. In +particular + +

+ + +

Shared storage

+ +

+Some Scheme implementations, e.g. guile and T, provide ways to construct +substrings that share storage with other strings. This facility is called +"shared-text substrings." Shared-text substrings can be used to eliminate the +allocation and copying time and space required to produce substrings, which +can be a tremendous savings for some applications, reducing a linear-time +operation to constant time. Additionally, some algorithms rely on the sharing +property of these substrings -- the application assumes that if the underlying +storage is mutated, then all strings sharing that storage will show the +change. However, shared-text substrings are not a common feature; most Scheme +implementations do not provide them. + +

+SRFI 13 takes a middle ground with respect to shared-text substrings. In +particular, a Scheme implementation does not need to have shared-text +substrings in order to implement this SRFI. + +

+There is an additional form of storage sharing enabled by some SRFI 13 +procedures, even without the benefit of shared-text substrings. In +some cases, some SRFI 13 routines are allowed to return as a result one +of the strings that was passed in as a parameter. For example, when +constructing a substring with the substring/shared procedure, if the +requested substring is the entire string, the procedure is permitted +simply to return the original value. That is, +

+(eq? s (substring/shared s 0 (string-length s))) => true or false
+
+

+whereas the R5RS +substring function is required to allocate a fresh copy +

+(eq? s (substring s 0 (string-length s))) => false.
+
+

+In keeping with SRFI 13's general approach to sharing, compliant +implementations are allowed, but not required, to provide this kind of +sharing. Hence, procedures may not rely upon sharing in these cases. +

+Most procedures that permit results to share storage with inputs have +equivalent procedures that require allocating fresh storage for results. +If an application wishes to be sure a new, fresh string is allocated, then +these "pure" procedures should be used. +

+ + + + + + + + + + + + + + + + + + + + + + +
Fresh copy guaranteedSharing permitted
string-copysubstring/shared
string-copystring-take string-take-right
string-copystring-drop string-drop-right
string-concatenatestring-concatenate/shared
string-appendstring-append/shared
string-concatenate-reverse + string-concatenate-reverse/shared
string-pad string-pad-right
string-trim string-trim-right
string-trim-both
string-filter string-delete
+
+ +

+On the other hand, the functionality is present to allow one to write +efficient code without shared-text substrings. You can write efficient code +that works by passing around start/end ranges indexing into a string instead +of simply building a shared-text substring. The API would be much simpler +without this consideration -- if we had cheap shared-text substrings, all the +start/end index parameters would vanish. However, since SRFI 13 does not +require implementations to provide shared-text substrings, the extended +API is provided. + + +

R4RS/R5RS procedures

+ +

+The R4RS and R5RS reports define 22 string procedures. The string-lib +package includes 8 of these exactly as defined, 3 in an extended, +backwards-compatible way, and drops the remaining 11 (whose functionality +is available via other bindings). + +

+The 8 procedures provided exactly as documented in the reports are + string?, + make-string, + string, + string-length, + string-ref, + string-set!, + string-append, and + list->string. + +

+The eleven functions not included are + string=?, string-ci=?, + string<?, string-ci<?, + string>?, string-ci>?, + string<=?, string-ci<=?, + string>=?, string-ci>=?, and + substring. +The string-lib package provides alternate bindings and extended functionality. + +

+Additionally, the three extended procedures are +

+string-fill! s char [start end] -> unspecified
+string->list s [start end] -> char-list
+string-copy  s [start end] -> string
+
+

+They are uniformly extended to take optional start/end parameters specifying +substring ranges. + + +

Extra-SRFI recommendations

+ +

+This SRFI recommends the following + +

+ +

+ These recommendations are not a part of the SRFI 13 spec. Note also that + requiring a Unicode/Latin-1/ASCII interface to integer/char mapping + functions does not imply anything about the actual underlying encodings of + characters. + + + +

Procedure Specification

+ +

+In the following procedure specifications: +

+

+Passing values to procedures with these parameters that do not satisfy these +types is an error. + +

+Parameters given in square brackets are optional. Unless otherwise noted in the +text describing the procedure, any prefix of these optional parameters may +be supplied, from zero arguments to the full list. When a procedure returns +multiple values, this is shown by listing the return values in square +brackets, as well. So, for example, the procedure with signature +

+halts? f [x init-store] -> [boolean integer]
+
+would take one (f), two (f, x) +or three (f, x, init-store) input parameters, +and return two values, a boolean and an integer. + +

+A parameter followed by "..." means zero-or-more elements. +So the procedure with the signature +

+sum-squares x ...  -> number
+
+takes zero or more arguments (x ...), +while the procedure with signature +
+spell-check doc dict1 dict2 ... -> string-list
+
+takes two required parameters +(doc and dict1) +and zero or more optional parameters (dict2 ...). + +

+If a procedure is said to return "unspecified," this means that nothing at +all is said about what the procedure returns. Such a procedure is not even +required to be consistent from call to call. It is simply required to +return a value (or values) that may be passed to a command continuation, +e.g. as the value of an expression appearing as a non-terminal +subform of a begin expression. +Note that in + R5RS, +this restricts such a procedure to returning a single value; +non-R5RS systems may not even provide this restriction. + + +

Main procedures

+ +

+In a Scheme system that has a module or package system, these procedures +should be contained in a module named "string-lib". + + +

Predicates

+ +
+ +
+ +string? obj -> boolean +
+ [R5RS] +Returns #t if obj is a string, otherwise returns #f. + + +
+ +string-null? s -> boolean +
+ Is s the empty string? +
+ + +
+ + +string-every char/char-set/pred s [start end] -> value +
string-any char/char-set/pred s [start end] -> value +
+ Checks to see if the given criteria is true of every / any character in s, + proceeding from left (index start) to right (index end). + +

+ If char/char-set/pred is a character, it is tested for equality with + the elements of s. + +

+ If char/char-set/pred is a character set, the elements of s are tested + for membership in the set. + +

+ If char/char-set/pred is a predicate procedure, it is applied to the + elements of s. The predicate is "witness-generating:" + +

+ If string-every or string-any apply the predicate to the final element + of the selected sequence (i.e., s[end-1]), that final application is a + tail call. + +

+ The names of these procedures do not end with a question mark -- this is to + indicate that, in the predicate case, they do not return a simple boolean + (#t or #f), but a general value. +

+ + + +

Constructors

+ +
+ +
+ +make-string len [char] -> string +
+ [R5RS] + make-string returns a newly allocated string of length len. If + char is given, then all elements of the string are initialized + to char, otherwise the contents of the string are unspecified. + + +
+ +string char1 ... -> string +
+ [R5RS] + Returns a newly allocated string composed of the argument characters. + + +
+ +string-tabulate proc len -> string +
+ Proc is an integer->char procedure. Construct a string of size len + by applying proc to each index to produce the corresponding string + element. The order in which proc is applied to the indices is not + specified. + +
+ + +

List & string conversion

+ +
+ + +
+ + +string->list s [start end] -> char-list +
list->string char-list -> string +
+ [R5RS+] + string->list returns a newly allocated list of the characters + that make up the given string. list->string returns a newly + allocated string formed from the characters in the list char-list, + which must be a list of characters. string->list and list->string + are inverses so far as equal? is concerned. + +

+ string->list is extended from the R5RS definition to take optional + start/end arguments. + + +

+ +reverse-list->string char-list -> string +
+ An efficient implementation of (compose list->string reverse): +
+(reverse-list->string '(#\a #\B #\c)) -> "cBa"
+
+ This is a common idiom in the epilog of string-processing loops + that accumulate an answer in a reverse-order list. (See also + string-concatenate-reverse for the "chunked" variant.) + + +
+ +string-join string-list [delimiter grammar] -> string +
+ This procedure is a simple unparser --- it pastes strings together using + the delimiter string. + +

+ The grammar argument is a symbol that determines how the delimiter is + used, and defaults to 'infix. + +

+ + The delimiter is the string used to delimit elements; it defaults to + a single space " ". +
+(string-join '("foo" "bar" "baz") ":")         => "foo:bar:baz"
+(string-join '("foo" "bar" "baz") ":" 'suffix) => "foo:bar:baz:"
+
+;; Infix grammar is ambiguous wrt empty list vs. empty string,
+(string-join '()   ":") => ""
+(string-join '("") ":") => ""
+
+;; but suffix & prefix grammars are not.
+(string-join '()   ":" 'suffix) => ""
+(string-join '("") ":" 'suffix) => ":"
+
+
+ + + +

Selection

+ +
+ +
+ +string-length s -> integer +
+ [R5RS] + Returns the number of characters in the string s. + + +
+ +string-ref s i -> char +
+ [R5RS] + Returns character s[i] using zero-origin indexing. + I must be a valid index of s. + + +
+ + +string-copy s [start end] -> string +
substring/shared s start [end] -> string +
+ [R5RS+] + substring/shared returns a string whose contents are the characters of s + beginning with index start (inclusive) and ending with index end + (exclusive). It differs from the R5RS substring in two ways: + + +

+ string-copy is extended from its R5RS definition by the addition of + its optional start/end parameters. In contrast to substring/shared, + it is guaranteed to produce a freshly-allocated string. + +

+ Use string-copy when you want to indicate explicitly in your code that you + wish to allocate new storage; use substring/shared when you don't care if + you get a fresh copy or share storage with the original string. +

+(string-copy "Beta substitution") => "Beta substitution"
+(string-copy "Beta substitution" 1 10) 
+    => "eta subst"
+(string-copy "Beta substitution" 5) => "substitution"
+
+ + +
+ +string-copy! target tstart s [start end] -> unspecified +
+ Copy the sequence of characters from index range [start,end) in + string s to string target, beginning at index tstart. The characters + are copied left-to-right or right-to-left as needed -- the copy is + guaranteed to work, even if target and s are the same string. + +

+ It is an error if the copy operation runs off the end of the target + string, e.g. +

+(string-copy! (string-copy "Microsoft") 0
+              "Regional Microsoft Operating Companies") => error
+
+ + + +
+ + + + +string-take s nchars -> string +
string-drop s nchars -> string +
string-take-right s nchars -> string +
string-drop-right s nchars -> string +
+ string-take returns the first nchars of s; + string-drop returns all but the first nchars of s. + string-take-right returns the last nchars of s; + string-drop-right returns all but the last nchars of s. + If these procedures produce the entire string, they may return either + s or a copy of s; in some implementations, proper substrings may share + memory with s. +
+(string-take "Pete Szilagyi" 6) => "Pete S"
+(string-drop "Pete Szilagyi" 6) => "zilagyi"
+
+(string-take-right "Beta rules" 5) => "rules"
+(string-drop-right "Beta rules" 5) => "Beta "
+
+ + It is an error to take or drop more characters than are in the string: +
+(string-take "foo" 37) => error
+
+ + +
+ + +string-pad s len [char start end] -> string +
string-pad-right s len [char start end] -> string +
+ Build a string of length len comprised of s padded on the left (right) + by as many occurrences of the character char as needed. If s has more + than len chars, it is truncated on the left (right) to length len. Char + defaults to #\space. + +

+ If len <= end-start, the returned value is allowed to share storage + with s, or be exactly s (if len = end-start). +

+(string-pad     "325" 5) => "  325"
+(string-pad   "71325" 5) => "71325"
+(string-pad "8871325" 5) => "71325"
+
+ + +
+ + + +string-trim       s [char/char-set/pred start end] -> string +
string-trim-right s [char/char-set/pred start end] -> string +
string-trim-both  s [char/char-set/pred start end] -> string +
+ Trim s by skipping over all characters on the left / on the right / + on both sides that satisfy the second parameter char/char-set/pred: + + Char/char-set/pred defaults to the character set char-set:whitespace + defined in SRFI 14. + +

+ If no trimming occurs, these functions may return either s or a copy of s; + in some implementations, proper substrings may share memory with s. + +

+(string-trim-both "  The outlook wasn't brilliant,  \n\r")
+    => "The outlook wasn't brilliant,"
+
+
+ + + +

Modification

+ +
+ + +
+ +string-set! s i char -> unspecified +
+ [R5RS] + I must be a valid index of s. string-set! stores char in + element i of s. Constant string literals appearing in code are + immutable; it is an error to use them in a string-set!. + +
+(define (f) (make-string 3 #\*))
+(define (g) "***")
+(string-set! (f) 0 #\?)                ==>  unspecified
+(string-set! (g) 0 #\?)                ==>  error
+(string-set! (symbol->string 'immutable)
+             3
+             #\?)                      ==>  error
+
+ + +
+ +string-fill! s char [start end] -> unspecified +
+ [R5RS+] + Stores char in every element of s. + +

+ string-fill is extended from the R5RS definition to take optional + start/end arguments. + +

+ + +

Comparison

+ +
+ + +
+ + +string-compare    s1 s2 proc< proc= proc> [start1 end1 start2 end2] -> values +
string-compare-ci s1 s2 proc< proc= proc> [start1 end1 start2 end2] -> values +
+Apply proc<, proc=, or proc> + to the mismatch index, depending +upon whether s1 is less than, equal to, or greater than s2. +The "mismatch index" is the largest index i such that for +every 0 <= j < i, +s1[j] = s2[j] +-- that is, i is the first position that doesn't match. + +

+string-compare-ci is the case-insensitive variant. Case-insensitive +comparison is done by case-folding characters with the operation +

+(char-downcase (char-upcase c))
+
+where the two case-mapping operations are assumed to be 1-1, locale- and +context-insensitive, and compatible with the 1-1 case mappings specified +by Unicode's UnicodeData.txt table: + + +

+The optional start/end indices restrict the comparison to the indicated +substrings of s1 and s2. The mismatch index is always an index into s1; +in the case of proc=, it is always end1; +we observe the protocol +in this redundant case for uniformity. + +

+(string-compare "The cat in the hat" "abcdefgh" 
+                values values values
+                4 6         ; Select "ca" 
+                2 4)        ; & "cd"
+    => 5    ; Index of S1's "a"
+
+ +Comparison is simply done on individual code-points of the string. +True text collation is not handled by this SRFI. + + +
+ + + + + + +string=  s1 s2 [start1 end1 start2 end2] -> boolean +
string<> s1 s2 [start1 end1 start2 end2] -> boolean +
string<  s1 s2 [start1 end1 start2 end2] -> boolean +
string>  s1 s2 [start1 end1 start2 end2] -> boolean +
string<= s1 s2 [start1 end1 start2 end2] -> boolean +
string>= s1 s2 [start1 end1 start2 end2] -> boolean +
+ These procedures are the lexicographic extensions to strings of the + corresponding orderings on characters. For example, string< is the + lexicographic ordering on strings induced by the ordering char<? on + characters. If two strings differ in length but are the same up to + the length of the shorter string, the shorter string is considered to + be lexicographically less than the longer string. + +

+ The optional start/end indices restrict the comparison to the indicated + substrings of s1 and s2. + +

+ Comparison is simply done on individual code-points of the string. + True text collation is not handled by this SRFI. + + +

+ + + + + + +string-ci=  s1 s2 [start1 end1 start2 end2] -> boolean +
string-ci<> s1 s2 [start1 end1 start2 end2] -> boolean +
string-ci<  s1 s2 [start1 end1 start2 end2] -> boolean +
string-ci>  s1 s2 [start1 end1 start2 end2] -> boolean +
string-ci<= s1 s2 [start1 end1 start2 end2] -> boolean +
string-ci>= s1 s2 [start1 end1 start2 end2] -> boolean +
+ Case-insensitive variants. + +

+ Case-insensitive comparison is done by case-folding characters with + the operation +

+(char-downcase (char-upcase c))
+
+ where the two case-mapping operations are assumed to be 1-1, locale- and + context-insensitive, and compatible with the 1-1 case mappings specified + by Unicode's UnicodeData.txt table: + + + +
+ + +string-hash    s [bound start end] -> integer +
string-hash-ci s [bound start end] -> integer +
+Compute a hash value for the string s. +Bound is a non-negative +exact integer specifying the range of the hash function. A positive +value restricts the return value to the range [0,bound). + +

+If bound is either zero or not given, the implementation may use +an implementation-specific default value, chosen to be as large as +is efficiently practical. For instance, the default range might be chosen +for a given implementation to map all strings into the range of +integers that can be represented with a single machine word. + +

+The optional start/end indices restrict the hash operation to the +indicated substring of s. + +

+string-hash-ci is the case-insensitive variant. Case-insensitive +comparison is done by case-folding characters with the operation +

+(char-downcase (char-upcase c))
+
+where the two case-mapping operations are assumed to be 1-1, locale- and +context-insensitive, and compatible with the 1-1 case mappings specified +by Unicode's UnicodeData.txt table: + + +

+Invariants: +

+(<= 0 (string-hash s b) (- b 1)) ; When B > 0.
+(string=    s1 s2)  =>  (= (string-hash s1 b)    (string-hash s2 b))
+(string-ci= s1 s2)  =>  (= (string-hash-ci s1 b) (string-hash-ci s2 b))
+
+ +

+A legal but nonetheless discouraged implementation: +

+(define (string-hash    s . other-args) 1)
+(define (string-hash-ci s . other-args) 1)
+
+ +

+ Rationale: allowing the user to specify an explicit bound simplifies user + code by removing the mod operation that typically accompanies every hash + computation, and also may allow the implementation of the hash function to + exploit a reduced range to efficiently compute the hash value. + E.g., for + small bounds, the hash function may be computed in a fashion such that + intermediate values never overflow into bignum integers, allowing the + implementor to provide a fixnum-specific "fast path" for computing the + common cases very rapidly. + + +

+ + +

Prefixes & suffixes

+ +
+ +
+ + + + +string-prefix-length    s1 s2 [start1 end1 start2 end2] -> integer +
string-suffix-length    s1 s2 [start1 end1 start2 end2] -> integer +
string-prefix-length-ci s1 s2 [start1 end1 start2 end2] -> integer +
string-suffix-length-ci s1 s2 [start1 end1 start2 end2] -> integer +
+Return the length of the longest common prefix/suffix of the two strings. +For prefixes, this is equivalent to the "mismatch index" for the strings +(modulo the starti index offsets). + +

+The optional start/end indices restrict the comparison to the indicated +substrings of s1 and s2. + +

+string-prefix-length-ci and string-suffix-length-ci are the +case-insensitive variants. Case-insensitive comparison is done by +case-folding characters with the operation +

+(char-downcase (char-upcase c))
+
+where the two case-mapping operations are assumed to be 1-1, locale- and +context-insensitive, and compatible with the 1-1 case mappings specified +by Unicode's UnicodeData.txt table: + +Comparison is simply done on individual code-points of the string. + + +
+ + + + +string-prefix?    s1 s2 [start1 end1 start2 end2] -> boolean +
string-suffix?    s1 s2 [start1 end1 start2 end2] -> boolean +
string-prefix-ci? s1 s2 [start1 end1 start2 end2] -> boolean +
string-suffix-ci? s1 s2 [start1 end1 start2 end2] -> boolean +
+Is s1 a prefix/suffix of s2? + +

+The optional start/end indices restrict the comparison to the indicated +substrings of s1 and s2. + +

+string-prefix-ci? and string-suffix-ci? are the case-insensitive variants. +Case-insensitive comparison is done by case-folding characters with the +operation +

+(char-downcase (char-upcase c))
+
+where the two case-mapping operations are assumed to be 1-1, locale- and +context-insensitive, and compatible with the 1-1 case mappings specified +by Unicode's UnicodeData.txt table: + + +

+Comparison is simply done on individual code-points of the string. + +

+ + +

Searching

+ +
+ + +
+ + + + +string-index s char/char-set/pred [start end] -> integer or #f +
string-index-right s char/char-set/pred [start end] -> integer or #f +
string-skip s char/char-set/pred [start end] -> integer or #f +
string-skip-right s char/char-set/pred [start end] -> integer or #f +
+string-index (string-index-right) searches through the string from the +left (right), returning the index of the first occurrence of a character +which + +If no match is found, the functions return false. + +

+The start and end parameters specify the beginning and end indices of +the search; the search includes the start index, but not the end index. +Be careful of "fencepost" considerations: when searching right-to-left, +the first index considered is +

+ end-1 +
+whereas when searching left-to-right, the first index considered is +
+ start +
+That is, the start/end indices describe a same half-open interval +[start,end) in these procedures that they do +in all the other SRFI 13 procedures. + +

+The skip functions are similar, but use the complement of the criteria: +they search for the first char that doesn't satisfy the test. E.g., +to skip over initial whitespace, say +

+(cond ((string-skip s char-set:whitespace) =>
+       (lambda (i) ...)) ; s[i] is not whitespace.
+      ...)
+
+ + +
+ +string-count s char/char-set/pred [start end] -> integer +
+ Return a count of the number of characters in s that satisfy the + char/char-set/pred argument. If this argument is a procedure, + it is applied to the character as a predicate; if it is a character set, + the character is tested for membership; if it is a character, it is + used in an equality test. + + +
+ + +string-contains    s1 s2 [start1 end1 start2 end2] -> integer or false +
string-contains-ci s1 s2 [start1 end1 start2 end2] -> integer or false +
+Does string s1 contain string s2? + +

+Return the index in s1 where s2 occurs as a substring, or false. +The optional start/end indices restrict the operation to the +indicated substrings. + +

+The returned index is in the range [start1,end1). +A successful match must lie entirely in the +[start1,end1) range of s1. + +

+

+(string-contains "eek -- what a geek." "ee"
+                 12 18) ; Searches "a geek"
+    => 15
+
+ +

+string-contains-ci is the case-insensitive variant. Case-insensitive +comparison is done by case-folding characters with the operation +

+(char-downcase (char-upcase c))
+
+where the two case-mapping operations are assumed to be 1-1, locale- and +context-insensitive, and compatible with the 1-1 case mappings specified +by Unicode's UnicodeData.txt table: + + +

+Comparison is simply done on individual code-points of the string. + +

+The names of these procedures do not end with a question mark -- this is to +indicate that they do not return a simple boolean (#t or #f). Rather, +they return either false (#f) or an exact non-negative integer. + +

+ + +

Alphabetic case mapping

+ +
+ + +
+ + +string-titlecase  s [start end] -> string +
string-titlecase! s [start end] -> unspecified +
+For every character c in the selected range of s, +if c is preceded by a cased character, it is downcased; +otherwise it is titlecased. + +

+string-titlecase returns the result string and does not alter its s +parameter. string-titlecase! is the in-place side-effecting variant. + +

+

+(string-titlecase "--capitalize tHIS sentence.") =>
+  "--Capitalize This Sentence."
+
+(string-titlecase "see Spot run. see Nix run.") =>
+  "See Spot Run. See Nix Run."
+
+(string-titlecase "3com makes routers.") =>
+  "3Com Makes Routers."
+
+ +

+Note that if a start index is specified, then the character +preceding s[start] has no effect on the titlecase decision for +character s[start]: +

+(string-titlecase "greasy fried chicken" 2) => "Easy Fried Chicken"
+
+ +

+Titlecase and cased information must be compatible with the Unicode +specification. + + +

+ + + + +string-upcase  s [start end] -> string +
string-upcase! s [start end] -> unspecified +
string-downcase  s [start end] -> string +
string-downcase! s [start end] -> unspecified +
+ Raise or lower the case of the alphabetic characters in the string. + +

+ string-upcase and string-downcase return the result string and do not + alter their s parameter. string-upcase! and string-downcase! are the + in-place side-effecting variants. + +

+ These procedures use the locale- and context-insensitive 1-1 case mappings + defined by Unicode's UnicodeData.txt table: +

+ +
+ + +

Reverse & append

+ +
+ + +
+ + +string-reverse  s [start end] -> string +
string-reverse! s [start end] -> unspecified +
+Reverse the string. + +

+string-reverse returns the result string +and does not alter its s parameter. +string-reverse! is the in-place side-effecting variant. + +

+(string-reverse "Able was I ere I saw elba.") 
+    => ".able was I ere I saw elbA"
+
+;;; In-place rotate-left, the Bell Labs way:
+(lambda (s i)
+  (let ((i (modulo i (string-length s))))
+    (string-reverse! s 0 i)
+    (string-reverse! s i)
+    (string-reverse! s)))
+
+ +

+Unicode note: Reversing a string simply reverses the sequence of +code-points it contains. So a zero-width accent character a +coming after a base character b in string s +would come out before b in the reversed result. + + +

+ +string-append s1 ... -> string +
+ [R5RS] + Returns a newly allocated string whose characters form the + concatenation of the given strings. + + +
+ +string-concatenate string-list -> string +
+ Append the elements of string-list together into a single string. + Guaranteed to return a freshly allocated string. + +

+ Note that the (apply string-append string-list) + idiom is + not robust for long lists of strings, as some Scheme implementations + limit the number of arguments that may be passed to an n-ary procedure. + + +

+ + +string-concatenate/shared string-list -> string +
string-append/shared s1 ... -> string +
+ These two procedures are variants of string-concatenate + and string-append + that are permitted to return results that share storage with their + parameters. + In particular, if string-append/shared is applied to just + one argument, it may return exactly that argument, + whereas string-append is required to allocate a fresh string. + + +
+ + +string-concatenate-reverse string-list [final-string end] -> string +
string-concatenate-reverse/shared string-list [final-string end] -> string +
+With no optional arguments, these functions are equivalent to +
+(string-concatenate (reverse string-list))
+
+and +
+(string-concatenate/shared (reverse string-list))
+
+respectively. + +

+If the optional argument final-string is specified, it is consed +onto the beginning of string-list +before performing the list-reverse and string-concatenate operations. + +

+If the optional argument end is given, +only the first end characters +of final-string are added to the string list, thus producing +
+(string-concatenate 
+  (reverse (cons (substring/shared final-string 0 end)
+                 string-list)))
+
+E.g. +
+(string-concatenate-reverse '(" must be" "Hello, I") " going.XXXX" 7)
+  => "Hello, I must be going."
+
+ +

+This procedure is useful in the construction of procedures that +accumulate character data into lists of string buffers, and wish to +convert the accumulated data into a single string when done. + +

+Unicode note: Reversing a string simply reverses the sequence of +code-points it contains. +So a zero-width accent character ac coming after +a base character bc in string s would come out +before bc in the reversed result. + +

+ + +

Fold, unfold & map

+ +
+ + +
+ + +string-map  proc s [start end] -> string +
string-map! proc s [start end] -> unspecified +
+ Proc is a char->char procedure; it is mapped over s. + +

+ string-map returns the result string and does not alter its s parameter. + string-map! is the in-place side-effecting variant. + +

+ Note: The order in which proc is applied to the elements of + s is not specified. + + +

+ + +string-fold kons knil s [start end] -> value +
string-fold-right kons knil s [start end] -> value +
+These are the fundamental iterators for strings. + +

+The left-fold operator maps the kons procedure across the +string from left to right +

+(... (kons s[2] (kons s[1] (kons s[0] knil))))
+
+In other words, string-fold obeys the (tail) recursion +
+(string-fold kons knil s start end) =
+    (string-fold kons (kons s[start] knil) start+1 end)
+
+ +

+The right-fold operator maps the kons procedure across the +string from right to left +

+(kons s[0] (... (kons s[end-3] (kons s[end-2] (kons s[end-1] knil)))))
+
+obeying the (tail) recursion +
+(string-fold-right kons knil s start end) =
+    (string-fold-right kons (kons s[end-1] knil) start end-1)
+
+ +

+Examples: +

+;;; Convert a string to a list of chars.
+(string-fold-right cons '() s)
+
+;;; Count the number of lower-case characters in a string.
+(string-fold (lambda (c count)
+               (if (char-lower-case? c)
+                   (+ count 1)
+                   count))
+             0
+             s)
+
+;;; Double every backslash character in S.
+(let* ((ans-len (string-fold (lambda (c sum)
+                               (+ sum (if (char=? c #\\) 2 1)))
+                             0 s))
+       (ans (make-string ans-len)))
+  (string-fold (lambda (c i)
+                 (let ((i (if (char=? c #\\)
+                              (begin (string-set! ans i #\\) (+ i 1))
+                              i)))
+                   (string-set! ans i c)
+                   (+ i 1)))
+               0 s)
+  ans)
+
+ +

+The right-fold combinator is sometimes called a "catamorphism." + + +

+ +string-unfold p f g seed [base make-final] -> string +
+This is a fundamental constructor for strings. + + +

+More precisely, the following (simple, inefficient) definitions hold: + +

+;;; Iterative
+(define (string-unfold p f g seed base make-final)
+  (let lp ((seed seed) (ans base))
+    (if (p seed) 
+        (string-append ans (make-final seed))
+        (lp (g seed) (string-append ans (string (f seed)))))))
+                                    
+;;; Recursive
+(define (string-unfold p f g seed base make-final)
+  (string-append base
+                 (let recur ((seed seed))
+                   (if (p seed) (make-final seed)
+                       (string-append (string (f seed))
+                                      (recur (g seed)))))))
+
+

+string-unfold is a fairly powerful string constructor -- you can use it to +convert a list to a string, read a port into a string, reverse a string, +copy a string, and so forth. Examples: +

+(port->string p) = (string-unfold eof-object? values
+                                  (lambda (x) (read-char p))
+                                  (read-char p))
+
+(list->string lis) = (string-unfold null? car cdr lis)
+
+(string-tabulate f size) = (string-unfold (lambda (i) (= i size)) f add1 0)
+
+

+To map f over a list lis, producing a string: +

+(string-unfold null? (compose f car) cdr lis)
+
+

+Interested functional programmers may enjoy noting that +string-fold-right +and string-unfold are in some sense inverses. That is, given operations +knull?, kar, kdr, kons, and knil satisfying +

+(kons (kar x) (kdr x)) = x  and (knull? knil) = #t
+
+then +
+(string-fold-right kons knil (string-unfold knull? kar kdr x)) = x
+
+and +
+(string-unfold knull? kar kdr (string-fold-right kons knil s)) = s.
+
+ +The final string constructed does not share storage with either base +or the value produced by make-final. + +

+This combinator sometimes is called an "anamorphism." + +

+Note: implementations should take care that runtime stack limits do not +cause overflow when constructing large (e.g., megabyte) strings with +string-unfold. + + + +

+ +string-unfold-right p f g seed [base make-final] -> string +
+ This is a fundamental constructor for strings. + + +

+ More precisely, the following (simple, inefficient) definitions hold: +

+;;; Iterative
+(define (string-unfold-right p f g seed base make-final)
+  (let lp ((seed seed) (ans base))
+    (if (p seed) 
+        (string-append (make-final seed) ans)
+        (lp (g seed) (string-append (string (f seed)) ans)))))
+
+;;; Recursive
+(define (string-unfold-right p f g seed base make-final)
+  (string-append (let recur ((seed seed))
+                   (if (p seed) (make-final seed)
+                       (string-append (recur (g seed))
+                                      (string (f seed)))))
+                 base))
+
+ Interested functional programmers may enjoy noting that + string-fold + and string-unfold-right are in some sense inverses. + That is, given operations knull?, kar, kdr, kons, and knil satisfying +
+(kons (kar x) (kdr x)) = x and (knull? knil) = #t +
+ then +
+(string-fold kons knil (string-unfold-right knull? kar kdr x)) = x
+
+ and +
+(string-unfold-right knull? kar kdr (string-fold kons knil s)) = s.
+
+ + The final string constructed does not share storage with either base + or the value produced by make-final. + +

+ Note: implementations should take care that runtime stack limits do not + cause overflow when constructing large (e.g., megabyte) strings with + string-unfold-right. + + + +

+ +string-for-each proc s [start end] -> unspecified +
+ Apply proc to each character in s. + string-for-each is required to iterate from start to end + in increasing order. + + +
+ +string-for-each-index proc s [start end] -> unspecified +
+Apply proc to each index of s, in order. The optional start/end +pairs restrict the endpoints of the loop. This is simply a +method of looping over a string that is guaranteed to be safe +and correct. + +Example: +
+(let* ((len (string-length s))
+       (ans (make-string len)))
+  (string-for-each-index
+      (lambda (i) (string-set! ans (- len i) (string-ref s i)))
+      s)
+  ans)
+
+ +
+ + +

Replicate & rotate

+ +
+ + +
+ +xsubstring s from [to start end] -> string +
+ This is the "extended substring" procedure that implements replicated + copying of a substring of some string. + +

+ S is a string; start and end are optional arguments that demarcate + a substring of s, defaulting to 0 and the length of s (i.e., the whole + string). Replicate this substring up and down index space, in both the + positive and negative directions. For example, if s = "abcdefg", start=3, + and end=6, then we have the conceptual bidirectionally-infinite string +

+ + + + + +
... d e f d e f d e f d e f d e f d e f d ... +
... -9 -8 -7 -6 -5 -4 -3 -2 -1 0 +1 +2 +3 +4 +5 +6 +7 +8 +9 ... +
+
+ + xsubstring returns the substring of this string beginning at index from, + and ending at to + (which defaults to from+(end-start)). + +

+ You can use xsubstring to perform a variety of tasks: +

+ +

+ Note that +

+ +

+ It is an error if start=end -- although this is allowed by special + dispensation when from=to. + + +

+ +string-xcopy! target tstart s sfrom [sto start end] -> unspecified +
+ Exactly the same as xsubstring, but the extracted text is written + into the string target starting at index tstart. + This operation is not defined if (eq? target s) + or these two arguments + share storage -- you cannot copy a string on top of itself. + +
+ + +

Miscellaneous: insertion, parsing

+ +
+ + +
+ +string-replace s1 s2 start1 end1 [start2 end2] -> string +
+ Returns +
+(string-append (substring/shared s1 0 start1)
+               (substring/shared s2 start2 end2)
+               (substring/shared s1 end1 (string-length s1)))
+
+ + That is, the segment of characters in s1 from start1 to end1 + is replaced by the segment of characters in s2 from start2 to end2. + If start1=end1, this simply splices the s2 characters into s1 at the + specified index. + +

+ Examples: +

+(string-replace "The TCL programmer endured daily ridicule."
+                "another miserable perl drone" 4 7 8 22 ) =>
+    "The miserable perl programmer endured daily ridicule."
+
+(string-replace "It's easy to code it up in Scheme." "lots of fun" 5 9) =>
+    "It's lots of fun to code it up in Scheme."
+
+(define (string-insert s i t) (string-replace s t i i))
+
+(string-insert "It's easy to code it up in Scheme." 5 "really ") =>
+    "It's really easy to code it up in Scheme."
+
+ + +
+ +string-tokenize s [token-set start end] -> list +
+ Split the string s into a list of substrings, where each substring is + a maximal non-empty contiguous sequence of characters from the character set + token-set. + + +

+ This function provides a minimal parsing facility for simple applications. + More sophisticated parsers that handle quoting and backslash effects can + easily be constructed using regular-expression systems; be careful not + to use string-tokenize in contexts where more serious parsing is needed. + +

+(string-tokenize "Help make programs run, run, RUN!") =>
+  ("Help" "make" "programs" "run," "run," "RUN!")
+
+ +
+ + +

Filtering & deleting

+ +
+ + +
+ + +string-filter char/char-set/pred s [start end] -> string +
string-delete char/char-set/pred s [start end] -> string +
+ Filter the string s, retaining only those characters that + satisfy / do not satisfy the char/char-set/pred argument. If + this argument is a procedure, it is applied to the character + as a predicate; if it is a char-set, the character is tested + for membership; if it is a character, it is used in an equality test. + +

+ If the string is unaltered by the filtering operation, these + functions may return either s or a copy of s. + + +

+ + +

Low-level procedures

+

+The following procedures are useful for writing other string-processing +functions. In a Scheme system that has a module or package system, these +procedures should be contained in a module named "string-lib-internals". + + +

Start/end optional-argument parsing & checking utilities

+ + +
+ + +
+ + +string-parse-start+end proc s args -> [rest start end] +
string-parse-final-start+end proc s args -> [start end] +
+ string-parse-start+end may be used to parse a pair of optional start/end + arguments from an argument list, defaulting them to 0 and the length of + some string s, respectively. Let the length of string s be slen. + + +

+ If any of the checks fail, an error condition is raised, and proc is used + as part of the error condition -- it should be the client procedure whose + argument list string-parse-start+end is parsing. + +

+ string-parse-final-start+end is exactly the same, except that the args list + passed to it is required to be of length two or less; if it is longer, + an error condition is raised. It may be used when the optional start/end + parameters are final arguments to the procedure. + +

+ Note that in all cases, these functions ensure that s is a string + (by necessity, since all cases apply string-length to s either to + default end or to bounds-check it). + +

+ +let-string-start+end (start end [rest]) proc-exp s-exp args-exp body ... -> value(s) +
+ + [Syntax] + Syntactic sugar for an application of string-parse-start+end or + string-parse-final-start+end. + +

+ If a rest variable is given, the form is equivalent to +

+(call-with-values
+    (lambda () (string-parse-start+end proc-exp s-exp args-exp))
+  (lambda (rest start end) body ...))
+
+ +

+ If no rest variable is given, the form is equivalent to +

+(call-with-values
+    (lambda () (string-parse-final-start+end proc-exp s-exp args-exp))
+  (lambda (start end) body ...))
+
+ + +
+ + +check-substring-spec proc s start end -> unspecified +
substring-spec-ok? s start end -> boolean +
+ Check values s, start and end to ensure they specify a valid substring. + This means that s is a string, start and end are exact integers, and + 0 <= start <= end <= + (string-length s) + +

+ If the values are not proper +

+ Otherwise, substring-spec-ok? returns true, and check-substring-spec + simply returns (what it returns is not specified). + +
+ + + +

Knuth-Morris-Pratt searching

+

+The Knuth-Morris-Pratt string-search algorithm is a method of rapidly scanning +a sequence of text for the occurrence of some fixed string. It has the +advantage of never requiring backtracking -- hence, it is useful for searching +not just strings, but also other sequences of text that do not support +backtracking or random-access, such as input ports. These routines package up +the initialisation and searching phases of the algorithm for general use. They +also support searching through sequences of text that arrive in buffered +chunks, in that intermediate search state can be carried across applications +of the search loop from the end of one buffer application to the next. + +

+A second critical property of KMP search is that it requires the allocation of +auxiliary memory proportional to the length of the pattern, but constant +in the size of the character type. Alternate searching algorithms frequently +require the construction of a table with an entry for every possible +character -- which can be prohibitively expensive in a 16- or 32-bit character +representation. + +

+ +
+ +make-kmp-restart-vector s [c= start end] -> integer-vector +
+Build a Knuth-Morris-Pratt "restart vector," which is useful for quickly +searching character sequences for the occurrence of string s (or the +substring of s demarcated by the optional start/end parameters, if +provided). C= is a character-equality function used to construct the +restart vector. It defaults to char=?; use char-ci=? instead for +case-folded string search. + +

+The definition of the restart vector rv for string s is: +If we have matched chars 0..i-1 of s against some search string ss, and +s[i] doesn't match ss[k], then reset i := rv[i], and try again to +match ss[k]. +If rv[i] = -1, +then punt ss[k] completely, and move on to +ss[k+1] and s[0]. + +

+In other words, if you have matched the first i chars of s, but +the i+1'th char doesn't match, +rv[i] tells you what the next-longest +prefix of s is that you have matched. + +

+The following string-search function shows how a restart vector is used to +search. Note the attractive feature of the search process: it is "on +line," that is, it never needs to back up and reconsider previously seen +data. It simply consumes characters one-at-a-time until declaring a complete +match or reaching the end of the sequence. Thus, it can be easily adapted to +search other character sequences (such as ports) that do not provide random +access to their contents. + +

+(define (find-substring pattern source start end)
+  (let ((plen (string-length pattern))
+        (rv (make-kmp-restart-vector pattern)))
+
+    ;; The search loop. SJ & PJ are redundant state.
+    (let lp ((si start) (pi 0)
+             (sj (- end start))     ; (- end si)  -- how many chars left.
+             (pj plen))             ; (- plen pi) -- how many chars left.
+
+      (if (= pi plen) (- si plen)                   ; Win.
+
+          (and (<= pj sj)                           ; Lose.
+
+               (if (char=? (string-ref source si)           ; Test.
+                           (string-ref pattern pi))
+                   (lp (+ 1 si) (+ 1 pi) (- sj 1) (- pj 1)) ; Advance.
+
+                   (let ((pi (vector-ref rv pi)))           ; Retreat.
+                     (if (= pi -1)
+                         (lp (+ si 1)  0   (- sj 1)  plen)  ; Punt.
+                         (lp si        pi  sj        (- plen pi))))))))))
+
+ +

+The optional start/end parameters restrict the restart vector to the +indicated substring of pat; rv is end - start elements long. If start > 0, +then rv is offset by start elements from pat. +That is, rv[i] describes +pattern element pat[i + start]. +Elements of rv are themselves indices +that range just over [0, end-start), +not [start, end). + +

+Rationale: the actual value of rv is "position independent" -- it +does not depend on where in the pat string the pattern occurs, but +only on the actual characters comprising the pattern. + + +

+ +kmp-step pat rv c i c= p-start -> integer +
+This function encapsulates the work performed by one step of the +KMP string search; it can be used to scan strings, input ports, +or other on-line character sources for fixed strings. + +

+Pat is the non-empty string specifying the text for which we are searching. +Rv is the Knuth-Morris-Pratt restart vector for the pattern, +as constructed by make-kmp-restart-vector. +The pattern begins at pat[p-start], and is +(string-length rv) characters long. +C= is the character-equality function used to construct the +restart vector, typically char=? or char-ci=?. + +

+Suppose the pattern is N characters in length: +pat[p-start, p-start + n). +We have already matched i characters: +pat[p-start, p-start + i). +(P-start is typically zero.) +C is the next character in the input stream. kmp-step +returns the new i value -- that is, how much of the pattern we have +matched, including character c. +When i reaches n, the entire pattern has been matched. + +

+Thus a typical search loop looks like this: +

+(let lp ((i 0))
+  (or (= i n)                           ; Win -- #t
+      (and (not (end-of-stream))        ; Lose -- #f
+           (lp (kmp-step pat rv (get-next-character) i char=? 0)))))
+
+ +

+Example: +

+;; Read chars from IPORT until we find string PAT or hit EOF.
+(define (port-skip pat iport)
+  (let* ((rv (make-kmp-restart-vector pat))
+         (patlen (string-length pat)))
+    (let lp ((i 0) (nchars 0))
+      (if (= i patlen) nchars                    ; Win -- nchars skipped
+          (let ((c (read-char iport)))
+            (if (eof-object? c) c                ; Fail -- EOF
+                (lp (kmp-step pat rv c i char=? 0) ; Continue
+                    (+ nchars 1))))))))
+
+ +

+This procedure could be defined as follows: +

+(define (kmp-step pat rv c i c= p-start)
+  (let lp ((i i))
+    (if (c= c (string-ref pat (+ i p-start)))     ; Match =>
+        (+ i 1)                                   ;   Done.
+        (let ((i (vector-ref rv i)))              ; Back up in PAT.
+          (if (= i -1) 0                          ; Can't back up more.
+              (lp i)))))))                        ; Keep going.
+
+ +

+Rationale: this procedure takes no optional arguments because it +is intended as an inner-loop primitive and we do not want any +run-time penalty for optional-argument parsing and defaulting, +nor do we wish barriers to procedure integration/inlining. + + +

+ +string-kmp-partial-search pat rv s i [c= p-start s-start s-end] -> integer +
+Applies kmp-step across s; +optional s-start/s-end bounds parameters +restrict search to a substring of s. +The pattern is (vector-length rv) characters long; +optional p-start index indicates non-zero start of pattern +in pat. + +

+Suppose plen = (vector-length rv) +is the length of the pattern. +I is an integer index into the pattern +(that is, 0 <= i < plen) +indicating how much of the pattern has already been matched. +(This means the pattern must be non-empty -- plen > 0.) + +

+ +Hence: + + +

+This utility is designed to allow searching for occurrences of a fixed +string that might extend across multiple buffers of text. This is +why, for example, we do not provide the index of the start of the +match on success -- it may have occurred in a previous buffer. + +

+To search a character sequence that arrives in "chunks," write a +loop of this form: +

+(let lp ((i 0))
+  (and (not (end-of-data?))             ; Lose -- return #f.
+       (let* ((buf (get-next-chunk))    ; Get or fill up the buffer.
+              (i (string-kmp-partial-search pat rv buf i)))
+         (if (< i 0) (- i)              ; Win -- return end index.
+             (lp i)))))                 ; Keep looking.
+
+Modulo start/end optional-argument parsing, this procedure could +be defined as follows: +
+(define (string-kmp-partial-search pat rv s i c= p-start s-start s-end)
+  (let ((patlen (vector-length rv)))
+    (let lp ((si s-start)       ; An index into S.
+             (vi i))            ; An index into RV.
+      (cond ((= vi patlen) (- si))      ; Win.
+            ((= si end) vi)             ; Ran off the end.
+            (else (lp (+ si 1)          ; Match s[si] & loop.
+                      (kmp-step pat rv (string-ref s si)
+                                vi c= p-start)))))))
+
+
+ + +

Reference implementation

+ +

+This SRFI comes with a reference implementation. It can be found at: +

+ http://srfi.schemers.org/srfi-13/srfi-13.scm +
+

+I have placed this source on the Net with an unencumbered, "open" copyright. +The prefix/suffix and comparison routines in this code had (extremely distant) +origins in MIT Scheme's string lib, and were substantially reworked by myself. +Being derived from that code, they are covered by the MIT Scheme copyright, +which is a generic BSD-style open-source copyright. See the source file for +details. + +

+The KMP string-search code was influenced by implementations written by +Stephen Bevan, Brian Denheyer and Will Fitzgerald. However, this version was +written from scratch by myself. + +

+The remainder of the code was written by myself for scsh or for this SRFI; I +have placed this code under the scsh copyright, which is also a generic +BSD-style open-source copyright. + +

+The code is written for portability and should be straightforward to port to +any Scheme. The source comments contains detailed notes describing the non-R5RS +dependencies. + +

+The library is written for clarity and well-commented; the current source is +approximately 1000 lines of source code and 1000 lines of comments and white +space. It is also written for efficiency. Fast paths are provided for common +cases. This is not to say that the implementation can't be tuned up for a +specific Scheme implementation. There are notes in the comments addressing +ways implementors can tune the reference implementation for performance. + +

+In short, I've written the reference implementation to make it as painless +as possible for an implementor -- or a regular programmer -- to adopt this +library and get good results with it. + + + +

Acknowledgements

+ +

+The design of this library benefited greatly from the feedback provided during +the SRFI discussion phase. Among those contributing thoughtful commentary and +suggestions, both on the mailing list and by private discussion, were Paolo +Amoroso, Lars Arvestad, Alan Bawden, Jim Bender, Dan Bornstein, Per Bothner, +Will Clinger, Brian Denheyer, Mikael Djurfeldt, Kent Dybvig, Sergei Egorov, +Marc Feeley, Matthias Felleisen, Will Fitzgerald, Matthew Flatt, Arthur A. +Gleckler, Ben Goetter, Sven Hartrumpf, Erik Hilsdale, Richard Kelsey, Oleg +Kiselyov, Bengt Kleberg, Donovan Kolbly, Bruce Korb, Shriram Krishnamurthi, +Bruce Lewis, Tom Lord, Brad Lucier, Dave Mason, David Rush, Klaus Schilling, +Jonathan Sobel, Mike Sperber, Mikael Staldal, Vladimir Tsyshevsky, Donald +Welsh, and Mike Wilson. I am grateful to them for their assistance. + +

+I am also grateful the authors, implementors and documentors of all the systems +mentioned in the introduction. Aubrey Jaffer and Kent Pitman should be noted +for their work in producing Web-accessible versions of the R5RS and Common +Lisp spec, which was a tremendous aid. + +

+This is not to imply that these individuals necessarily endorse the final +results, of course. + +

+During this document's long development period, great patience was exhibited +by Mike Sperber, who is the editor for the SRFI, and by Hillary Sullivan, +who is not. + + +

References & links

+ +
+ +
[Case-map] +
+ Case mappings.
+ Unicode Technical Report 21.
+ http://www.unicode.org/unicode/reports/tr21/ + +
[CommonLisp]
+
Common Lisp: the Language.
+Guy L. Steele Jr. (editor).
+Digital Press, Maynard, Mass., second edition 1990.
+Available at +http://www.elwood.com/alu/table/references.htm#cltl2. +

+ +The Common Lisp "HyperSpec," produced by Kent Pitman, is essentially +the ANSI spec for Common Lisp: + +http://www.harlequin.com/education/books/HyperSpec/. + +

[Java] +
+ The following URLs provide documentation on relevant Java classes.
+ + http://java.sun.com/products/jdk/1.2/docs/api/java/lang/Character.html +
+ http://java.sun.com/products/jdk/1.2/docs/api/java/lang/String.html +
+ http://java.sun.com/products/jdk/1.2/docs/api/java/lang/StringBuffer.html +
+ http://java.sun.com/products/jdk/1.2/docs/api/java/text/Collator.html +
+ http://java.sun.com/products/jdk/1.2/docs/api/java/text/package-summary.html + +
[MIT-Scheme] +
+ http://www.swiss.ai.mit.edu/projects/scheme/ + +
[R5RS]
+
Revised5 report on the algorithmic language Scheme.
+ R. Kelsey, W. Clinger, J. Rees (editors).
+ Higher-Order and Symbolic Computation, Vol. 11, No. 1, September, 1998.
+ and ACM SIGPLAN Notices, Vol. 33, No. 9, October, 1998.
+ Available at + http://www.schemers.org/Documents/Standards/. + +
[SRFI]
+
+ The SRFI web site.
+ http://srfi.schemers.org/ + +
[SRFI-13]
+
+ SRFI-13: String libraries.
+ http://srfi.schemers.org/srfi-13/ + +
+
+ This document, in HTML: +
+ http://srfi.schemers.org/srfi-13/srfi-13.html + +
+ This document, in plain text format: +
+ http://srfi.schemers.org/srfi-13/srfi-13.txt + +
Source code for the reference implementation: +
+ + http://srfi.schemers.org/srfi-13/srfi-13.scm + +
Scheme 48 module specification, with typings: +
+ + http://srfi.schemers.org/srfi-13/srfi-13-s48-module.scm +
+
+ +
[SRFI-14] +
+ SRFI-14: Character-set library.
+ http://srfi.schemers.org/srfi-14/
+ The SRFI 14 char-set library defines a character-set data type, + which is used by some procedures in this library. + +
[Unicode] +
+ http://www.unicode.org/ + +
[UnicodeData] +
+ The Unicode character database.
+ ftp://ftp.unicode.org/Public/UNIDATA/UnicodeData.txt +
+ ftp://ftp.unicode.org/Public/UNIDATA/UnicodeData.html + +
+ + +

Copyright

+ +

+Certain portions of this document -- the specific, marked segments of text +describing the R5RS procedures -- were adapted with permission from the R5RS +report. + +

+All other text is copyright (C) Olin Shivers (1998, 1999, 2000). +All Rights Reserved. + +

+Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: +

+

+The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. +

+

+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +

+ + + + + diff --git a/doc/srfi-std/srfi-14.html b/doc/srfi-std/srfi-14.html new file mode 100644 index 0000000000..6a046de52f --- /dev/null +++ b/doc/srfi-std/srfi-14.html @@ -0,0 +1,2023 @@ + + + + + + + + + SRFI 14: Character-set Library + + + + + + + + + + +

Title

+ +
SRFI 14: Character-set Library
+ + +

Author

+ +Olin Shivers + +

Status

+This SRFI is currently in ``final'' status. To see an explanation of each status that a SRFI can hold, see here. +You can access the discussion via the archive of the mailing list. +

+ + +

Table of contents

+ + + + + +

Abstract

+

+ +The ability to efficiently represent and manipulate sets of characters is an +unglamorous but very useful capability for text-processing code -- one that +tends to pop up in the definitions of other libraries. Hence it is useful to +specify a general substrate for this functionality early. This SRFI defines a +general library that provides this functionality. + +It is accompanied by a reference implementation for the spec. The reference +implementation is fairly efficient, straightforwardly portable, and has a +"free software" copyright. The implementation is tuned for "small" 7 or 8 +bit character types, such as ASCII or Latin-1; the data structures and +algorithms would have to be altered for larger 16 or 32 bit character types +such as Unicode -- however, the specs have been carefully designed with these +larger character types in mind. + +Several forthcoming SRFIs can be defined in terms of this one: +

+ + + +

Variable Index

+

+Here is the complete set of bindings -- procedural and otherwise -- +exported by this library. In a Scheme system that has a module or package +system, these procedures should be contained in a module named "char-set-lib". + +

+
+
Predicates & comparison +
+
+char-set? char-set= char-set<= char-set-hash
+
+ +
Iterating over character sets +
+
+char-set-cursor char-set-ref char-set-cursor-next end-of-char-set? 
+char-set-fold char-set-unfold char-set-unfold!
+char-set-for-each char-set-map
+
+ +
Creating character sets +
+
+char-set-copy char-set
+
+list->char-set  string->char-set
+list->char-set! string->char-set!
+    
+char-set-filter  ucs-range->char-set 
+char-set-filter! ucs-range->char-set!
+
+->char-set
+
+ +
Querying character sets +
+
+char-set->list char-set->string
+char-set-size char-set-count char-set-contains?
+char-set-every char-set-any
+
+ +
Character-set algebra +
+
+char-set-adjoin  char-set-delete
+char-set-adjoin! char-set-delete!
+
+char-set-complement  char-set-union  char-set-intersection
+char-set-complement! char-set-union! char-set-intersection!
+
+char-set-difference  char-set-xor  char-set-diff+intersection
+char-set-difference! char-set-xor! char-set-diff+intersection!
+
+ +
Standard character sets +
+
+char-set:lower-case  char-set:upper-case  char-set:title-case
+char-set:letter      char-set:digit       char-set:letter+digit
+char-set:graphic     char-set:printing    char-set:whitespace
+char-set:iso-control char-set:punctuation char-set:symbol
+char-set:hex-digit   char-set:blank       char-set:ascii
+char-set:empty       char-set:full
+
+ +
+
+ + +

Rationale

+ +

+The ability to efficiently manipulate sets of characters is quite +useful for text-processing code. Encapsulating this functionality in +a general, efficiently implemented library can assist all such code. +This library defines a new data structure to represent these sets, called +a "char-set." The char-set type is distinct from all other types. + +

+This library is designed to be portable across implementations that use +different character types and representations, especially ASCII, Latin-1 +and Unicode. Some effort has been made to preserve compatibility with Java +in the Unicode case (see the definition of char-set:whitespace for the +single real deviation). + + +

Linear-update operations

+ +

+The procedures of this SRFI, by default, are "pure functional" -- they do not +alter their parameters. However, this SRFI defines a set of "linear-update" +procedures which have a hybrid pure-functional/side-effecting semantics: they +are allowed, but not required, to side-effect one of their parameters in order +to construct their result. An implementation may legally implement these +procedures as pure, side-effect-free functions, or it may implement them using +side effects, depending upon the details of what is the most efficient or +simple to implement in terms of the underlying representation. + +

+The linear-update routines all have names ending with "!". + +

+Clients of these procedures may not rely upon these procedures working by +side effect. For example, this is not guaranteed to work: +

+(let* ((cs1 (char-set #\a #\b #\c))      ; cs1 = {a,b,c}.
+       (cs2 (char-set-adjoin! cs1 #\d))) ; Add d to {a,b,c}.
+  cs1) ; Could be either {a,b,c} or {a,b,c,d}.
+
+

+However, this is well-defined: +

+(let ((cs (char-set #\a #\b #\c)))
+  (char-set-adjoin! cs #\d)) ; Add d to {a,b,c}.
+
+ +

+So clients of these procedures write in a functional style, but must +additionally be sure that, when the procedure is called, there are no other +live pointers to the potentially-modified character set (hence the term +"linear update"). + +

+There are two benefits to this convention: +

+ +

+Note that pure functional representations are the right thing for +ASCII- or Latin-1-based Scheme implementations, since a char-set can +be represented in an ASCII Scheme with 4 32-bit words. Pure set-algebra +operations on such a representation are very fast and efficient. Programmers +who code using linear-update operations are guaranteed the system will +provide the best implementation across multiple platforms. + +

+In practice, these procedures are most useful for efficiently constructing +character sets in a side-effecting manner, in some limited local context, +before passing the character set outside the local construction scope to be +used in a functional manner. + +

+Scheme provides no assistance in checking the linearity of the potentially +side-effected parameters passed to these functions --- there's no linear +type checker or run-time mechanism for detecting violations. (But +sophisticated programming environments, such as DrScheme, might help.) + + +

Extra-SRFI recommendations

+

+Users are cautioned that the R5RS predicates +

+char-alphabetic?
+char-numeric?
+char-whitespace?
+char-upper-case?
+char-lower-case?
+
+
+

+may or may not be in agreement with the SRFI 14 base character sets +

+ +char-set:letter
+char-set:digit
+char-set:whitespace
+char-set:upper-case
+char-set:lower-case
+
+
+

+Implementors are strongly encouraged to bring these predicates into +agreement with the base character sets of this SRFI; not to do so risks +major confusion. + + + +

Specification

+

+In the following procedure specifications: +

+ +

+Passing values to procedures with these parameters that do not satisfy these +types is an error. + +

+Unless otherwise noted in the specification of a procedure, procedures +always return character sets that are distinct (from the point of view +of the linear-update operations) from the parameter character sets. For +example, char-set-adjoin is guaranteed to provide a fresh character set, +even if it is not given any character parameters. + +

+Parameters given in square brackets are optional. Unless otherwise noted in the +text describing the procedure, any prefix of these optional parameters may +be supplied, from zero arguments to the full list. When a procedure returns +multiple values, this is shown by listing the return values in square +brackets, as well. So, for example, the procedure with signature +

+halts? f [x init-store] -> [boolean integer]
+
+would take one (f), two (f, x) +or three (f, x, init-store) input parameters, +and return two values, a boolean and an integer. + +

+A parameter followed by "..." means zero-or-more elements. +So the procedure with the signature +

+sum-squares x ...  -> number
+
+takes zero or more arguments (x ...), +while the procedure with signature +
+spell-check doc dict1 dict2 ... -> string-list
+
+takes two required parameters +(doc and dict1) +and zero or more optional parameters (dict2 ...). + + + +

General procedures

+
+ + +
+ +char-set? obj -> boolean +
+ + Is the object obj a character set? + + +
+ +char-set= cs1 ... -> boolean +
+ Are the character sets equal? +

+ Boundary cases: +

+(char-set=) => true
+(char-set= cs) => true
+
+ +

+ Rationale: transitive binary relations are generally extended to n-ary + relations in Scheme, which enables clearer, more concise code to be + written. While the zero-argument and one-argument cases will almost + certainly not arise in first-order uses of such relations, they may well + arise in higher-order cases or macro-generated code. + E.g., consider +

+(apply char-set= cset-list)
+
+

+ This is well-defined if the list is empty or a singleton list. Hence + we extend these relations to any number of arguments. Implementors + have reported actual uses of n-ary relations in higher-order cases + allowing for fewer than two arguments. The way of Scheme is to handle the + general case; we provide the fully general extension. +

+ A counter-argument to this extension is that + R5RS's + transitive binary arithmetic relations + (=, <, etc.) + require at least two arguments, hence + this decision is a break with the prior convention -- although it is + at least one that is backwards-compatible. + + +

+ +char-set<= cs1 ... -> boolean +
+ Returns true if every character set csi is + a subset of character set csi+1. + +

+Boundary cases: +

+(char-set<=) => true
+(char-set<= cs) => true
+
+

+Rationale: See char-set= for discussion of zero- and one-argument +applications. Consider testing a list of char-sets for monotonicity +with +

+(apply char-set<= cset-list)
+
+ + +
+ +char-set-hash cs [bound] -> integer +
+ Compute a hash value for the character set cs. + Bound is a non-negative + exact integer specifying the range of the hash function. A positive + value restricts the return value to the range [0,bound). + +

+ If bound is either zero or not given, the implementation may use + an implementation-specific default value, chosen to be as large as + is efficiently practical. For instance, the default range might be chosen + for a given implementation to map all strings into the range of + integers that can be represented with a single machine word. + + +

+ Invariant: +

+(char-set= cs1 cs2) => (= (char-set-hash cs1 b) (char-set-hash cs2 b))
+
+ +

+ A legal but nonetheless discouraged implementation: +

+(define (char-set-hash cs . maybe-bound) 1)
+
+ +

+ Rationale: allowing the user to specify an explicit bound simplifies user + code by removing the mod operation that typically accompanies every hash + computation, and also may allow the implementation of the hash function to + exploit a reduced range to efficiently compute the hash value. + E.g., for + small bounds, the hash function may be computed in a fashion such that + intermediate values never overflow into bignum integers, allowing the + implementor to provide a fixnum-specific "fast path" for computing the + common cases very rapidly. + +

+ + +

Iterating over character sets

+ +
+ +
+ + + + +char-set-cursor cset -> cursor +
+char-set-ref cset cursor -> char +
+char-set-cursor-next cset cursor -> cursor +
+end-of-char-set? cursor -> boolean +
+ Cursors are a low-level facility for iterating over the characters in a + set. A cursor is a value that indexes a character in a char set. + char-set-cursor produces a new cursor for a given char set. + The set element indexed by the cursor is fetched with + char-set-ref. + A cursor index is incremented with char-set-cursor-next; + in this way, code can step through every character in a char set. + Stepping a cursor "past the end" of a char set produces a cursor that + answers true to end-of-char-set?. + It is an error to pass such a cursor to char-set-ref or to + char-set-cursor-next. + +

+ A cursor value may not be used in conjunction with a different character + set; if it is passed to char-set-ref or + char-set-cursor-next with + a character set other than the one used to create it, the results and + effects are undefined. + +

+ Cursor values are not necessarily distinct from other types. + They may be + integers, linked lists, records, procedures or other values. This license + is granted to allow cursors to be very "lightweight" values suitable for + tight iteration, even in fairly simple implementations. + +

+ Note that these primitives are necessary to export an iteration facility + for char sets to loop macros. + +

+ Example: +

+(define cs (char-set #\G #\a #\T #\e #\c #\h))
+
+;; Collect elts of CS into a list.
+(let lp ((cur (char-set-cursor cs)) (ans '()))
+  (if (end-of-char-set? cur) ans
+      (lp (char-set-cursor-next cs cur)
+          (cons (char-set-ref cs cur) ans))))
+  => (#\G #\T #\a #\c #\e #\h)
+
+;; Equivalently, using a list unfold (from SRFI 1):
+(unfold-right end-of-char-set? 
+              (curry char-set-ref cs)
+	      (curry char-set-cursor-next cs)
+	      (char-set-cursor cs))
+  => (#\G #\T #\a #\c #\e #\h)
+
+ +

+ Rationale: Note that the cursor API's four functions "fit" the functional + protocol used by the unfolders provided by the list, string and char-set + SRFIs (see the example above). By way of contrast, here is a simpler, + two-function API that was rejected for failing this criterion. Besides + char-set-cursor, it provided a single + function that mapped a cursor and a character set to two values, the + indexed character and the next cursor. If the cursor had exhausted the + character set, then this function returned false instead of the character + value, and another end-of-char-set cursor. In this way, the other three + functions of the current API were combined together. + + +

+ +char-set-fold kons knil cs -> object +
+ This is the fundamental iterator for character sets. Applies the function + kons across the character set cs using initial state value knil. That is, + if cs is the empty set, the procedure returns knil. Otherwise, some + element c of cs is chosen; + let cs' be the remaining, unchosen characters. + The procedure returns +
+(char-set-fold kons (kons c knil) cs')
+
+

+ Examples: +

+;; CHAR-SET-MEMBERS
+(lambda (cs) (char-set-fold cons '() cs))
+
+;; CHAR-SET-SIZE
+(lambda (cs) (char-set-fold (lambda (c i) (+ i 1)) 0 cs))
+
+;; How many vowels in the char set?
+(lambda (cs) 
+  (char-set-fold (lambda (c i) (if (vowel? c) (+ i 1) i))
+                 0 cs))
+
+ + +
+ + +char-set-unfold  f p g seed [base-cs] -> char-set +
char-set-unfold! f p g seed base-cs -> char-set +
+ This is a fundamental constructor for char-sets. + + +

+ More precisely, the following definitions hold, ignoring the + optional-argument issues: + +

+(define (char-set-unfold p f g seed base-cs) 
+  (char-set-unfold! p f g seed (char-set-copy base-cs)))
+
+(define (char-set-unfold! p f g seed base-cs)
+  (let lp ((seed seed) (cs base-cs))
+        (if (p seed) cs                                 ; P says we are done.
+            (lp (g seed)                                ; Loop on (G SEED).
+                (char-set-adjoin! cs (f seed))))))      ; Add (F SEED) to set.
+
+ + (Note that the actual implementation may be more efficient.) + +

+ Examples: +

                         
+(port->char-set p) = (char-set-unfold eof-object? values
+                                      (lambda (x) (read-char p))
+                                      (read-char p))
+
+(list->char-set lis) = (char-set-unfold null? car cdr lis)
+
+ +
+ +char-set-for-each proc cs -> unspecified +
+ Apply procedure proc to each character in the character set cs. + Note that the order in which proc is applied to the characters in the + set is not specified, and may even change from one procedure application + to another. + +

+ Nothing at all is specified about the value returned by this procedure; it + is not even required to be consistent from call to call. It is simply + required to be a value (or values) that may be passed to a command + continuation, e.g. as the value of an expression appearing as a + non-terminal subform of a begin expression. + Note that in + R5RS, + this restricts the procedure to returning a single value; + non-R5RS systems may not even provide this restriction. + + +

+ +char-set-map proc cs -> char-set +
+ proc is a char->char procedure. Apply it to all the characters in + the char-set cs, and collect the results into a new character set. + +

+ Essentially lifts proc from a char->char procedure to a char-set -> + char-set procedure. + +

+ Example: +

+(char-set-map char-downcase cset)
+
+
+ + + +

Creating character sets

+
+ + +
+ +char-set-copy cs -> char-set +
+ Returns a copy of the character set cs. "Copy" means that if either the + input parameter or the result value of this procedure is passed to one of + the linear-update procedures described below, the other character set is + guaranteed not to be altered. + +

+ A system that provides pure-functional implementations of the + linear-operator suite could implement this procedure as the identity + function -- so copies are not guaranteed to be distinct by eq?. + + +

+ +char-set char1 ... -> char-set +
+ Return a character set containing the given characters. + + +
+ + +list->char-set  char-list [base-cs] -> char-set +
list->char-set! char-list base-cs -> char-set +
+ Return a character set containing the characters in the list of + characters char-list. + +

+ If character set base-cs is provided, the characters from char-list + are added to it. list->char-set! is allowed, but not required, + to side-effect and reuse the storage in base-cs; + list->char-set produces a fresh character set. + + +

+ + +string->char-set  s [base-cs] -> char-set +
string->char-set! s base-cs -> char-set +
+ + Return a character set containing the characters in the string s. + +

+ If character set base-cs is provided, the characters from s are added to + it. string->char-set! is allowed, but not required, to side-effect and + reuse the storage in base-cs; string->char-set produces a fresh character + set. + + +

+ + +char-set-filter  pred cs [base-cs] -> char-set +
char-set-filter! pred cs base-cs -> char-set +
+ + Returns a character set containing every character c + in cs such that (pred c) + returns true. + +

+ If character set base-cs is provided, the characters specified + by pred are added to it. + char-set-filter! is allowed, but not required, + to side-effect and reuse the storage in base-cs; + char-set-filter produces a fresh character set. + +

+ An implementation may not save away a reference to pred and + invoke it after char-set-filter or + char-set-filter! returns -- that is, "lazy," + on-demand implementations are not allowed, as pred may have + external dependencies on mutable data or have other side-effects. + +

+ Rationale: This procedure provides a means of converting a character + predicate into its equivalent character set; the cs parameter + allows the programmer to bound the predicate's domain. Programmers should + be aware that filtering a character set such as char-set:full + could be a very expensive operation in an implementation that provided an + extremely large character type, such as 32-bit Unicode. An earlier draft + of this library provided a simple predicate->char-set + procedure, which was rejected in favor of char-set-filter for + this reason. + + + +

+ + +ucs-range->char-set  lower upper [error? base-cs] -> char-set +
ucs-range->char-set! lower upper error? base-cs -> char-set +
+ Lower and upper are exact non-negative integers; + lower <= upper. + +

+ Returns a character set containing every character whose ISO/IEC 10646 + UCS-4 code lies in the half-open range [lower,upper). + +

+ +

+ If character set base-cs is provided, the characters specified by the + range are added to it. ucs-range->char-set! is allowed, but not required, + to side-effect and reuse the storage in base-cs; + ucs-range->char-set produces a fresh character set. + +

+ Note that ASCII codes are a subset of the Latin-1 codes, which are in turn + a subset of the 16-bit Unicode codes, which are themselves a subset of the + 32-bit UCS-4 codes. We commit to a specific encoding in this routine, + regardless of the underlying representation of characters, so that client + code using this library will be portable. I.e., a conformant Scheme + implementation may use EBCDIC or SHIFT-JIS to encode characters; it must + simply map the UCS characters from the given range into the native + representation when possible, and report errors when not possible. + + +

+ +->char-set x -> char-set +
+ Coerces x into a char-set. + X may be a string, character or + char-set. A string is converted to the set of its constituent characters; + a character is converted to a singleton set; a char-set is returned + as-is. + This procedure is intended for use by other procedures that want to + provide "user-friendly," wide-spectrum interfaces to their clients. + +
+ + +

Querying character sets

+
+ + +
+ +char-set-size cs -> integer +
+ Returns the number of elements in character set cs. + + +
+ +char-set-count pred cs -> integer +
+ Apply pred to the chars of character set cs, and return the number + of chars that caused the predicate to return true. + + +
+ +char-set->list cs -> character-list +
+ This procedure returns a list of the members of character set cs. + The order in which cs's characters appear in the list is not defined, + and may be different from one call to another. + + +
+ +char-set->string cs -> string +
+ This procedure returns a string containing the members of character set cs. + The order in which cs's characters appear in the string is not defined, + and may be different from one call to another. + + +
+ +char-set-contains? cs char -> boolean +
+ This procedure tests char for membership in character set cs. + +

+ The MIT Scheme character-set package called this procedure + char-set-member?, but the argument order isn't consistent with the name. + + +

+ + +char-set-every pred cs -> boolean +
char-set-any   pred cs -> boolean +
+ The char-set-every procedure returns true if predicate pred + returns true of every character in the character set cs. + Likewise, char-set-any applies pred to every character in + character set cs, and returns the first true value it finds. + If no character produces a true value, it returns false. + The order in which these procedures sequence through the elements of + cs is not specified. + +

+ Note that if you need to determine the actual character on which a + predicate returns true, use char-set-any and arrange for the predicate + to return the character parameter as its true value, e.g. +

+(char-set-any (lambda (c) (and (char-upper-case? c) c)) 
+              cs)
+
+
+ + +

Character-set algebra

+
+ + +
+ + +char-set-adjoin cs char1 ... -> char-set +
char-set-delete cs char1 ... -> char-set +
+ Add/delete the chari characters to/from character set cs. + + +
+ + +char-set-adjoin! cs char1 ... -> char-set +
char-set-delete! cs char1 ... -> char-set +
+ + Linear-update variants. These procedures are allowed, but not + required, to side-effect their first parameter. + + +
+ + + + + + +char-set-complement cs -> char-set +
char-set-union cs1 ... -> char-set +
char-set-intersection cs1 ... -> char-set +
char-set-difference cs1 cs2 ... -> char-set +
char-set-xor cs1 ... -> char-set +
char-set-diff+intersection cs1 cs2 ... -> [char-set char-set] +
+ These procedures implement set complement, union, intersection, + difference, and exclusive-or for character sets. The union, intersection + and xor operations are n-ary. The difference function is also n-ary, + associates to the left (that is, it computes the difference between + its first argument and the union of all the other arguments), + and requires at least one argument. + +

+ Boundary cases: +

+(char-set-union) => char-set:empty
+(char-set-intersection) => char-set:full
+(char-set-xor) => char-set:empty
+(char-set-difference cs) => cs
+
+ +

+ char-set-diff+intersection returns both the difference and the + intersection of the arguments -- it partitions its first parameter. + It is equivalent to +

+(values (char-set-difference cs1 cs2 ...)
+        (char-set-intersection cs1 (char-set-union cs2 ...)))
+
+ but can be implemented more efficiently. + +

+ Programmers should be aware that char-set-complement could potentially + be a very expensive operation in Scheme implementations that provide + a very large character type, such as 32-bit Unicode. If this is a + possibility, sets can be complimented with respect to a smaller + universe using char-set-difference. + + + +

+ + + + + + +char-set-complement! cs -> char-set +
char-set-union! cs1 cs2 ... -> char-set +
char-set-intersection! cs1 cs2 ... -> char-set +
char-set-difference! cs1 cs2 ... -> char-set +
char-set-xor! cs1 cs2 ... -> char-set +
char-set-diff+intersection! cs1 cs2 cs3 ... -> [char-set char-set] +
+ These are linear-update variants of the set-algebra functions. + They are allowed, but not required, to side-effect their first (required) + parameter. + +

+ char-set-diff+intersection! is allowed to side-effect both + of its two required parameters, cs1 + and cs2. +

+ + +

Standard character sets

+

+Several character sets are predefined for convenience: + + + + + + + + + + + + + + + + + + +

+ + + + + + + + + + + + + + + + + + +
char-set:lower-case Lower-case letters
char-set:upper-case Upper-case letters
char-set:title-case Title-case letters
char-set:letter Letters
char-set:digit Digits
char-set:letter+digit Letters and digits
char-set:graphic Printing characters except spaces
char-set:printing Printing characters including spaces
char-set:whitespace Whitespace characters
char-set:iso-control The ISO control characters
char-set:punctuation Punctuation characters
char-set:symbol Symbol characters
char-set:hex-digit A hexadecimal digit: 0-9, A-F, a-f
char-set:blank Blank characters -- horizontal whitespace
char-set:ascii All characters in the ASCII set.
char-set:empty Empty set
char-set:full All characters
+
+ +

+Note that there may be characters in char-set:letter that are neither upper or +lower case---this might occur in implementations that use a character type +richer than ASCII, such as Unicode. A "graphic character" is one that would +put ink on your page. While the exact composition of these sets may vary +depending upon the character type provided by the underlying Scheme system, +here are the definitions for some of the sets in an ASCII implementation: +

+ + + + + + + + + + + + + +
char-set:lower-case a-z
char-set:upper-case A-Z
char-set:letter A-Z and a-z
char-set:digit 0123456789
char-set:punctuation !"#%&'()*,-./:;?@[\]_{}
char-set:symbol $+<=>^`|~
char-set:whitespace Space, newline, tab, form feed,
vertical tab, carriage return
char-set:blank Space and tab
char-set:graphic letter + digit + punctuation + symbol
char-set:printing graphic + whitespace
char-set:iso-control ASCII 0-31 and 127
+
+ +

+Note that the existence of the char-set:ascii set implies that the underlying +character set is required to be at least as rich as ASCII (including +ASCII's control characters). + +

+Rationale: The name choices reflect a shift from the older "alphabetic/numeric" +terms found in +R5RS +and Posix to newer, Unicode-influenced "letter/digit" lexemes. + + +

+ Unicode, Latin-1 and ASCII definitions of the standard character sets +

+

+In Unicode Scheme implementations, the base character sets are compatible with +Java's Unicode specifications. For ASCII or Latin-1, we simply restrict the +Unicode set specifications to their first 128 or 256 codes, respectively. +Scheme implementations that are not based on ASCII, Latin-1 or Unicode should +attempt to preserve the sense or spirit of these definitions. + +

+The following descriptions frequently make reference to the "Unicode character +database." This is a file, available at URL +

+ +ftp://ftp.unicode.org/Public/UNIDATA/UnicodeData.txt +
+

+Each line contains a description of a Unicode character. The first +semicolon-delimited field of the line gives the hex value of the character's +code; the second field gives the name of the character, and the third field +gives a two-letter category. Other fields give simple 1-1 case-mappings for +the character and other information; see +

+ +ftp://ftp.unicode.org/Public/UNIDATA/UnicodeData.html +
+

+for further description of the file's format. Note in particular the +two-letter category specified in the the third field, which is referenced +frequently in the descriptions below. + + +

char-set:lower-case

+

+For Unicode, we follow Java's specification: a character is lowercase if +

+ +

+The lower-case ASCII characters are +

+ abcdefghijklmnopqrstuvwxyz +
+

+Latin-1 adds another 33 lower-case characters to the ASCII set: +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
00B5 MICRO SIGN
00DF LATIN SMALL LETTER SHARP S
00E0 LATIN SMALL LETTER A WITH GRAVE
00E1 LATIN SMALL LETTER A WITH ACUTE
00E2 LATIN SMALL LETTER A WITH CIRCUMFLEX
00E3 LATIN SMALL LETTER A WITH TILDE
00E4 LATIN SMALL LETTER A WITH DIAERESIS
00E5 LATIN SMALL LETTER A WITH RING ABOVE
00E6 LATIN SMALL LETTER AE
00E7 LATIN SMALL LETTER C WITH CEDILLA
00E8 LATIN SMALL LETTER E WITH GRAVE
00E9 LATIN SMALL LETTER E WITH ACUTE
00EA LATIN SMALL LETTER E WITH CIRCUMFLEX
00EB LATIN SMALL LETTER E WITH DIAERESIS
00EC LATIN SMALL LETTER I WITH GRAVE
00ED LATIN SMALL LETTER I WITH ACUTE
00EE LATIN SMALL LETTER I WITH CIRCUMFLEX
00EF LATIN SMALL LETTER I WITH DIAERESIS
00F0 LATIN SMALL LETTER ETH
00F1 LATIN SMALL LETTER N WITH TILDE
00F2 LATIN SMALL LETTER O WITH GRAVE
00F3 LATIN SMALL LETTER O WITH ACUTE
00F4 LATIN SMALL LETTER O WITH CIRCUMFLEX
00F5 LATIN SMALL LETTER O WITH TILDE
00F6 LATIN SMALL LETTER O WITH DIAERESIS
00F8 LATIN SMALL LETTER O WITH STROKE
00F9 LATIN SMALL LETTER U WITH GRAVE
00FA LATIN SMALL LETTER U WITH ACUTE
00FB LATIN SMALL LETTER U WITH CIRCUMFLEX
00FC LATIN SMALL LETTER U WITH DIAERESIS
00FD LATIN SMALL LETTER Y WITH ACUTE
00FE LATIN SMALL LETTER THORN
00FF LATIN SMALL LETTER Y WITH DIAERESIS
+
+

+Note that three of these have no corresponding Latin-1 upper-case character: +

+ + + + +
00B5 MICRO SIGN
00DF LATIN SMALL LETTER SHARP S
00FF LATIN SMALL LETTER Y WITH DIAERESIS
+
+

+(The compatibility micro character uppercases to the non-Latin-1 Greek capital +mu; the German sharp s character uppercases to the pair of characters "SS," +and the capital y-with-diaeresis is non-Latin-1.) + +

+(Note that the Java spec for lowercase characters given at +

+ +http://java.sun.com/docs/books/jls/html/javalang.doc4.html#14345 +
+

+is inconsistent. U+00B5 MICRO SIGN fulfills the requirements for a lower-case +character (as of Unicode 3.0), but is not given in the numeric list of +lower-case character codes.) + +

+(Note that the Java spec for isLowerCase() given at +

+ +http://java.sun.com/products/jdk/1.2/docs/api/java/lang/Character.html#isLowerCase(char) +
+

+gives three mutually inconsistent definitions of "lower case." The first is +the definition used in this SRFI. Following text says "A character is +considered to be lowercase if and only if it is specified to be lowercase by +the Unicode 2.0 standard (category Ll in the Unicode specification data +file)." The former spec excludes U+00AA FEMININE ORDINAL INDICATOR and +U+00BA MASCULINE ORDINAL INDICATOR; the later spec includes them. Finally, +the spec enumerates a list of characters in the Latin-1 subset; this list +excludes U+00B5 MICRO SIGN, which is included in both of the previous specs.) + + +

char-set:upper-case

+

+For Unicode, we follow Java's specification: a character is uppercase if +

+ +

+The upper-case ASCII characters are +

+ABCDEFGHIJKLMNOPQRSTUVWXYZ +
+

+Latin-1 adds another 30 upper-case characters to the ASCII set: +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
00C0 LATIN CAPITAL LETTER A WITH GRAVE
00C1 LATIN CAPITAL LETTER A WITH ACUTE
00C2 LATIN CAPITAL LETTER A WITH CIRCUMFLEX
00C3 LATIN CAPITAL LETTER A WITH TILDE
00C4 LATIN CAPITAL LETTER A WITH DIAERESIS
00C5 LATIN CAPITAL LETTER A WITH RING ABOVE
00C6 LATIN CAPITAL LETTER AE
00C7 LATIN CAPITAL LETTER C WITH CEDILLA
00C8 LATIN CAPITAL LETTER E WITH GRAVE
00C9 LATIN CAPITAL LETTER E WITH ACUTE
00CA LATIN CAPITAL LETTER E WITH CIRCUMFLEX
00CB LATIN CAPITAL LETTER E WITH DIAERESIS
00CC LATIN CAPITAL LETTER I WITH GRAVE
00CD LATIN CAPITAL LETTER I WITH ACUTE
00CE LATIN CAPITAL LETTER I WITH CIRCUMFLEX
00CF LATIN CAPITAL LETTER I WITH DIAERESIS
00D0 LATIN CAPITAL LETTER ETH
00D1 LATIN CAPITAL LETTER N WITH TILDE
00D2 LATIN CAPITAL LETTER O WITH GRAVE
00D3 LATIN CAPITAL LETTER O WITH ACUTE
00D4 LATIN CAPITAL LETTER O WITH CIRCUMFLEX
00D5 LATIN CAPITAL LETTER O WITH TILDE
00D6 LATIN CAPITAL LETTER O WITH DIAERESIS
00D8 LATIN CAPITAL LETTER O WITH STROKE
00D9 LATIN CAPITAL LETTER U WITH GRAVE
00DA LATIN CAPITAL LETTER U WITH ACUTE
00DB LATIN CAPITAL LETTER U WITH CIRCUMFLEX
00DC LATIN CAPITAL LETTER U WITH DIAERESIS
00DD LATIN CAPITAL LETTER Y WITH ACUTE
00DE LATIN CAPITAL LETTER THORN
+
+ +

char-set:title-case

+

+In Unicode, a character is titlecase if it has the category Lt in +the character attribute database. There are very few of these characters; +here is the entire 31-character list as of Unicode 3.0: +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
01C5 LATIN CAPITAL LETTER D WITH SMALL LETTER Z WITH CARON +
01C8 LATIN CAPITAL LETTER L WITH SMALL LETTER J +
01CB LATIN CAPITAL LETTER N WITH SMALL LETTER J +
01F2 LATIN CAPITAL LETTER D WITH SMALL LETTER Z +
1F88 GREEK CAPITAL LETTER ALPHA WITH PSILI AND PROSGEGRAMMENI +
1F89 GREEK CAPITAL LETTER ALPHA WITH DASIA AND PROSGEGRAMMENI +
1F8A GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA AND PROSGEGRAMMENI +
1F8B GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA AND PROSGEGRAMMENI +
1F8C GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA AND PROSGEGRAMMENI +
1F8D GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA AND PROSGEGRAMMENI +
1F8E GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI +
1F8F GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI +
1F98 GREEK CAPITAL LETTER ETA WITH PSILI AND PROSGEGRAMMENI +
1F99 GREEK CAPITAL LETTER ETA WITH DASIA AND PROSGEGRAMMENI +
1F9A GREEK CAPITAL LETTER ETA WITH PSILI AND VARIA AND PROSGEGRAMMENI +
1F9B GREEK CAPITAL LETTER ETA WITH DASIA AND VARIA AND PROSGEGRAMMENI +
1F9C GREEK CAPITAL LETTER ETA WITH PSILI AND OXIA AND PROSGEGRAMMENI +
1F9D GREEK CAPITAL LETTER ETA WITH DASIA AND OXIA AND PROSGEGRAMMENI +
1F9E GREEK CAPITAL LETTER ETA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI +
1F9F GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI +
1FA8 GREEK CAPITAL LETTER OMEGA WITH PSILI AND PROSGEGRAMMENI +
1FA9 GREEK CAPITAL LETTER OMEGA WITH DASIA AND PROSGEGRAMMENI +
1FAA GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA AND PROSGEGRAMMENI +
1FAB GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA AND PROSGEGRAMMENI +
1FAC GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA AND PROSGEGRAMMENI +
1FAD GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA AND PROSGEGRAMMENI +
1FAE GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI +
1FAF GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI +
1FBC GREEK CAPITAL LETTER ALPHA WITH PROSGEGRAMMENI +
1FCC GREEK CAPITAL LETTER ETA WITH PROSGEGRAMMENI +
1FFC GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI +
+
+

+There are no ASCII or Latin-1 titlecase characters. + + + +

char-set:letter

+

+In Unicode, a letter is any character with one of the letter categories +(Lu, Ll, Lt, Lm, Lo) in the Unicode character database. + +

+There are 52 ASCII letters +

+ abcdefghijklmnopqrstuvwxyz
+ ABCDEFGHIJKLMNOPQRSTUVWXYZ
+
+

+There are 117 Latin-1 letters. These are the 115 characters that are +members of the Latin-1 char-set:lower-case and char-set:upper-case sets, +plus +

+ + + +
00AA FEMININE ORDINAL INDICATOR
00BA MASCULINE ORDINAL INDICATOR
+
+

+(These two letters are considered lower-case by Unicode, but not by +Java or SRFI 14.) + + +

char-set:digit

+ +

+In Unicode, a character is a digit if it has the category Nd in +the character attribute database. In Latin-1 and ASCII, the only +such characters are 0123456789. In Unicode, there are other digit +characters in other code blocks, such as Gujarati digits and Tibetan +digits. + + + +

char-set:hex-digit

+

+The only hex digits are 0123456789abcdefABCDEF. + + + +

char-set:letter+digit

+

+The union of char-set:letter and char-set:digit. + + +

char-set:graphic

+

+A graphic character is one that would put ink on paper. The ASCII and Latin-1 +graphic characters are the members of +

+ + + + + +
char-set:letter
char-set:digit
char-set:punctuation
char-set:symbol
+
+ + +

char-set:printing

+

+A printing character is one that would occupy space when printed, i.e., +a graphic character or a space character. char-set:printing is the union +of char-set:whitespace and char-set:graphic. + + +

char-set:whitespace

+

+In Unicode, a whitespace character is either +

+ +

+There are 24 whitespace characters in Unicode 3.0: +

+ + + + + + + + + + + + + + + + + + + + + + + + + +
0009 HORIZONTAL TABULATION \t control-I
000A LINE FEED \n control-J
000B VERTICAL TABULATION \v control-K
000C FORM FEED \f control-L
000D CARRIAGE RETURN \r control-M
0020 SPACE Zs
00A0 NO-BREAK SPACE Zs
1680 OGHAM SPACE MARK Zs
2000 EN QUAD Zs
2001 EM QUAD Zs
2002 EN SPACE Zs
2003 EM SPACE Zs
2004 THREE-PER-EM SPACE Zs
2005 FOUR-PER-EM SPACE Zs
2006 SIX-PER-EM SPACE Zs
2007 FIGURE SPACE Zs
2008 PUNCTUATION SPACE Zs
2009 THIN SPACE Zs
200A HAIR SPACE Zs
200B ZERO WIDTH SPACE Zs
2028 LINE SEPARATOR Zl
2029 PARAGRAPH SEPARATOR Zp
202F NARROW NO-BREAK SPACE Zs
3000 IDEOGRAPHIC SPACE Zs
+
+

+The ASCII whitespace characters are the first six characters in the above list +-- line feed, horizontal tabulation, vertical tabulation, form feed, carriage +return, and space. These are also exactly the characters recognised by the +Posix isspace() procedure. Latin-1 adds the no-break space. + +

+Note: Java's isWhitespace() method is incompatible, including +

+ + + + + + +
0009 HORIZONTAL TABULATION (\t control-I)
001C FILE SEPARATOR (control-\)
001D GROUP SEPARATOR (control-])
001E RECORD SEPARATOR (control-^)
001F UNIT SEPARATOR (control-_)
+
+

+and excluding +

+ + +
00A0 NO-BREAK SPACE
+
+

+Java's excluding the no-break space means that tokenizers can simply break +character streams at "whitespace" boundaries. However, the exclusion introduces +exceptions in other places, e.g. char-set:printing is no longer simply the +union of char-set:graphic and char-set:whitespace. + + + +

char-set:iso-control

+

+The ISO control characters are the Unicode/Latin-1 characters in the ranges +[U+0000,U+001F] and [U+007F,U+009F]. + +

+ASCII restricts this set to the characters in the range [U+0000,U+001F] +plus the character U+007F. + +

+Note that Unicode defines other control characters which do not belong to this +set (hence the qualifying prefix "iso-" in the name). This restriction is +compatible with the Java IsISOControl() method. + + + +

char-set:punctuation

+

+In Unicode, a punctuation character is any character that has one of the +punctuation categories in the Unicode character database (Pc, Pd, Ps, +Pe, Pi, Pf, or Po.) + +

+ASCII has 23 punctuation characters: +

+!"#%&'()*,-./:;?@[\]_{}
+
+

+Latin-1 adds six more: +

+ +
00A1 INVERTED EXCLAMATION MARK +
00AB LEFT-POINTING DOUBLE ANGLE QUOTATION MARK +
00AD SOFT HYPHEN +
00B7 MIDDLE DOT +
00BB RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK +
00BF INVERTED QUESTION MARK +
+
+ +

+Note that the nine ASCII characters $+<=>^`|~ are not +punctuation. They are "symbols." + + + +

char-set:symbol

+

+In Unicode, a symbol is any character that has one of the symbol categories +in the Unicode character database (Sm, Sc, Sk, or So). There +are nine ASCII symbol characters: +

+$+<=>^`|~
+
+

+Latin-1 adds 18 more: +

+ + + + + + + + + + + + + + + + + + + +
00A2 CENT SIGN
00A3 POUND SIGN
00A4 CURRENCY SIGN
00A5 YEN SIGN
00A6 BROKEN BAR
00A7 SECTION SIGN
00A8 DIAERESIS
00A9 COPYRIGHT SIGN
00AC NOT SIGN
00AE REGISTERED SIGN
00AF MACRON
00B0 DEGREE SIGN
00B1 PLUS-MINUS SIGN
00B4 ACUTE ACCENT
00B6 PILCROW SIGN
00B8 CEDILLA
00D7 MULTIPLICATION SIGN
00F7 DIVISION SIGN
+
+ + +

char-set:blank

+ +

+Blank chars are horizontal whitespace. In Unicode, a blank character is either +

+ +

+There are eighteen blank characters in Unicode 3.0: +

+ + + + + + + + + + + + + + + + + + + +
0009 HORIZONTAL TABULATION \t control-I
0020 SPACE Zs
00A0 NO-BREAK SPACE Zs
1680 OGHAM SPACE MARK Zs
2000 EN QUAD Zs
2001 EM QUAD Zs
2002 EN SPACE Zs
2003 EM SPACE Zs
2004 THREE-PER-EM SPACE Zs
2005 FOUR-PER-EM SPACE Zs
2006 SIX-PER-EM SPACE Zs
2007 FIGURE SPACE Zs
2008 PUNCTUATION SPACE Zs
2009 THIN SPACE Zs
200A HAIR SPACE Zs
200B ZERO WIDTH SPACE Zs
202F NARROW NO-BREAK SPACE Zs
3000 IDEOGRAPHIC SPACE Zs
+
+

+The ASCII blank characters are the first two characters above -- +horizontal tab and space. Latin-1 adds the no-break space. + +

+Java doesn't have the concept of "blank" characters, so there are no +compatibility issues. + + + +

Reference implementation

+

+This SRFI comes with a reference implementation. It resides at: +

+ +http://srfi.schemers.org/srfi-14/srfi-14.scm +
+

+I have placed this source on the Net with an unencumbered, "open" copyright. +Some of the code in the reference implementation bears a distant family +relation to the MIT Scheme implementation, and being derived from that code, +is covered by the MIT Scheme copyright (which is a generic BSD-style +open-source copyright -- see the source file for details). The remainder of +the code was written by myself for scsh or for this SRFI; I have placed this +code under the scsh copyright, which is also a generic BSD-style open-source +copyright. + +

+The code is written for portability and should be simple to port to +any Scheme. It has only the following deviations from R4RS, clearly +discussed in the comments: +

+ +

+The library is written for clarity and well-commented; the current source is +about 375 lines of source code and 375 lines of comments and white space. +It is also written for efficiency. Fast paths are provided for common cases. + +

+This is not to say that the implementation can't be tuned up for +a specific Scheme implementation. There are notes in comments addressing +ways implementors can tune the reference implementation for performance. + +

+In short, I've written the reference implementation to make it as painless +as possible for an implementor -- or a regular programmer -- to adopt this +library and get good results with it. + +

+The code uses a rather simple-minded, inefficient representation for +ASCII/Latin-1 char-sets -- a 256-character string. The character whose code is +i is in the set if s[i] = ASCII 1 (soh, or ^a); +not in the set if s[i] = ASCII 0 (nul). +A much faster and denser representation would be 16 or 32 bytes worth +of bit string. A portable implementation using bit sets awaits standards for +bitwise logical-ops and byte vectors. + +

+"Large" character types, such as Unicode, should use a sparse representation, +taking care that the Latin-1 subset continues to be represented with a +dense 32-byte bit set. + + + +

Acknowledgements

+

+The design of this library benefited greatly from the feedback provided during +the SRFI discussion phase. Among those contributing thoughtful commentary and +suggestions, both on the mailing list and by private discussion, were Paolo +Amoroso, Lars Arvestad, Alan Bawden, Jim Bender, Dan Bornstein, Per Bothner, +Will Clinger, Brian Denheyer, Kent Dybvig, Sergei Egorov, Marc Feeley, +Matthias Felleisen, Will Fitzgerald, Matthew Flatt, Arthur A. Gleckler, Ben +Goetter, Sven Hartrumpf, Erik Hilsdale, Shiro Kawai, Richard Kelsey, Oleg +Kiselyov, Bengt Kleberg, Donovan Kolbly, Bruce Korb, Shriram Krishnamurthi, +Bruce Lewis, Tom Lord, Brad Lucier, Dave Mason, David Rush, Klaus Schilling, +Jonathan Sobel, Mike Sperber, Mikael Staldal, Vladimir Tsyshevsky, Donald +Welsh, and Mike Wilson. I am grateful to them for their assistance. + +

+I am also grateful the authors, implementors and documentors of all the +systems mentioned in the introduction. Aubrey Jaffer should be noted for his +work in producing Web-accessible versions of the R5RS spec, which was a +tremendous aid. + +

+This is not to imply that these individuals necessarily endorse the final +results, of course. + +

+During this document's long development period, great patience was exhibited +by Mike Sperber, who is the editor for the SRFI, and by Hillary Sullivan, +who is not. + + +

References & links

+ +
+
[Java] +
+ The following URLs provide documentation on relevant Java classes.
+ + http://java.sun.com/products/jdk/1.2/docs/api/java/lang/Character.html +
+ http://java.sun.com/products/jdk/1.2/docs/api/java/lang/String.html +
+ http://java.sun.com/products/jdk/1.2/docs/api/java/lang/StringBuffer.html +
+ http://java.sun.com/products/jdk/1.2/docs/api/java/text/Collator.html +
+ http://java.sun.com/products/jdk/1.2/docs/api/java/text/package-summary.html + +
[MIT-Scheme] +
+ http://www.swiss.ai.mit.edu/projects/scheme/ + +
[R5RS]
+
Revised5 report on the algorithmic language Scheme.
+ R. Kelsey, W. Clinger, J. Rees (editors).
+ Higher-Order and Symbolic Computation, Vol. 11, No. 1, September, 1998.
+ and ACM SIGPLAN Notices, Vol. 33, No. 9, October, 1998.
+ Available at + http://www.schemers.org/Documents/Standards/. + +
[SRFI]
+
+ The SRFI web site.
+ http://srfi.schemers.org/ + +
[SRFI-14]
+
+ SRFI-14: String libraries.
+ http://srfi.schemers.org/srfi-14/ + +
+
+ This document, in HTML: +
+ http://srfi.schemers.org/srfi-14/srfi-14.html + +
+ This document, in plain text format: +
+ http://srfi.schemers.org/srfi-14/srfi-14.txt + +
Source code for the reference implementation: +
+ + http://srfi.schemers.org/srfi-14/srfi-14.scm + +
Scheme 48 module specification, with typings: +
+ + http://srfi.schemers.org/srfi-14/srfi-14-s48-module.scm + +
Regression-test suite: +
+ http://srfi.schemers.org/srfi-14/srfi-14-tests.scm + +
+
+ +
[Unicode] +
+ http://www.unicode.org/ + +
[UnicodeData] +
+ The Unicode character database.
+ ftp://ftp.unicode.org/Public/UNIDATA/UnicodeData.txt +
+ ftp://ftp.unicode.org/Public/UNIDATA/UnicodeData.html +
+ + +

Copyright

+ +

+Certain portions of this document -- the specific, marked segments of text +describing the R5RS procedures -- were adapted with permission from the R5RS +report. + +

+All other text is copyright (C) Olin Shivers (1998, 1999, 2000). +All Rights Reserved. + +

+Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: +

+

+The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. +

+

+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +

+ + + + diff --git a/doc/srfi-std/srfi-16.html b/doc/srfi-std/srfi-16.html new file mode 100644 index 0000000000..a335320b87 --- /dev/null +++ b/doc/srfi-std/srfi-16.html @@ -0,0 +1,177 @@ + + + + SRFI 16: Syntax for procedures of variable arity + + + + +

Title

+ +SRFI 16: Syntax for procedures of variable arity. + +

Author

+ +Lars T Hansen + +

Status

+This SRFI is currently in ``final'' status. To see an explanation of each status that a SRFI can hold, see here. +You can access the discussion via the archive of the mailing list. +

+ +

Abstract

+ +CASE-LAMBDA, a syntax for procedures with a variable number of arguments, +is introduced. + +

Rationale

+ +CASE-LAMBDA reduces the clutter of procedures that execute different +code depending on the number of arguments they were passed; it is +a pattern-matching mechanism that matches on the number of arguments. +CASE-LAMBDA is available in some Scheme systems. + +

While CASE-LAMBDA can be implemented as a macro using only facilities +available in R5RS Scheme, it admits considerable implementation-specific +optimization. + + +

Specification

+ +
+
+(CASE-LAMBDA <clause> ...) +
Syntax +
+

Each <clause> should have the form (<formals> <body>), where + <formals> is a formal arguments list as for LAMBDA, cf section + 4.1.4 of the R5RS. Each <body> is a <tail-body>, cf section + 3.5 of the R5RS. + +

A CASE-LAMBDA expression evaluates to a procedure that accepts + a variable number of arguments and is lexically scoped in the + same manner as procedures resulting from LAMBDA expressions. + When the procedure is called with some arguments V1 .. Vk, then + the first <clause> for which the arguments agree with <formals> + is selected, where agreement is specified as for the <formals> + of a LAMBDA expression. The variables of <formals> are bound + to fresh locations, the values V1 .. Vk are stored in those + locations, the <body> is evaluated in the extended environment, + and the results of <body> are returned as the results of the + procedure call. + +

It is an error for the arguments not to agree with the <formals> + of any <clause>. + +

+             (define plus
+               (case-lambda 
+                 (() 0)
+                 ((x) x)
+                 ((x y) (+ x y))
+                 ((x y z) (+ (+ x y) z))
+                 (args (apply + args))))
+
+             (plus)                     --> 0
+             (plus 1)                   --> 1
+             (plus 1 2 3)               --> 6
+
+             ((case-lambda 
+               ((a) a)
+               ((a b) (* a b)))
+              1 2 3)                    --> error
+
+
+ +

Implementation

+ +The following implementation is written in R5RS Scheme. It is not +compatible with the IEEE Scheme standard because the IEEE standard does +not contain the high-level macro system. + +

The implementation assumes that some top-level names defined by the +R5RS are bound to their original values. + +

+

+;; This code is in the public domain.
+
+(define-syntax case-lambda
+  (syntax-rules ()
+    ((case-lambda 
+      (?a1 ?e1 ...) 
+      ?clause1 ...)
+     (lambda args
+       (let ((l (length args)))
+         (case-lambda "CLAUSE" args l 
+           (?a1 ?e1 ...)
+           ?clause1 ...))))
+    ((case-lambda "CLAUSE" ?args ?l 
+      ((?a1 ...) ?e1 ...) 
+      ?clause1 ...)
+     (if (= ?l (length '(?a1 ...)))
+         (apply (lambda (?a1 ...) ?e1 ...) ?args)
+         (case-lambda "CLAUSE" ?args ?l 
+           ?clause1 ...)))
+    ((case-lambda "CLAUSE" ?args ?l
+      ((?a1 . ?ar) ?e1 ...) 
+      ?clause1 ...)
+     (case-lambda "IMPROPER" ?args ?l 1 (?a1 . ?ar) (?ar ?e1 ...) 
+       ?clause1 ...))
+    ((case-lambda "CLAUSE" ?args ?l 
+      (?a1 ?e1 ...)
+      ?clause1 ...)
+     (let ((?a1 ?args))
+       ?e1 ...))
+    ((case-lambda "CLAUSE" ?args ?l)
+     (error "Wrong number of arguments to CASE-LAMBDA."))
+    ((case-lambda "IMPROPER" ?args ?l ?k ?al ((?a1 . ?ar) ?e1 ...)
+      ?clause1 ...)
+     (case-lambda "IMPROPER" ?args ?l (+ ?k 1) ?al (?ar ?e1 ...) 
+      ?clause1 ...))
+    ((case-lambda "IMPROPER" ?args ?l ?k ?al (?ar ?e1 ...) 
+      ?clause1 ...)
+     (if (>= ?l ?k)
+         (apply (lambda ?al ?e1 ...) ?args)
+         (case-lambda "CLAUSE" ?args ?l 
+           ?clause1 ...)))))
+
+ +

Copyright

+

Copyright (C) Lars T Hansen (1999). All Rights Reserved.

+ +

+Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: +

+

+The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. +

+

+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +

+ +
+
Editor: Mike Sperber
+ + +Last modified: Mon Apr 19 20:38:48 CEST 2004 + + + diff --git a/doc/srfi-std/srfi-17.html b/doc/srfi-std/srfi-17.html new file mode 100644 index 0000000000..0bc010c751 --- /dev/null +++ b/doc/srfi-std/srfi-17.html @@ -0,0 +1,285 @@ + + + + SRFI 17: Generalized set! + + + + +

Title

+ +SRFI 17: Generalized set! + +

Author

+ +Per Bothner + +

Status

+ +This SRFI is currently in ``final'' status. To see an explanation of each status that a SRFI can hold, see here. +You can access the discussion via the archive of the mailing list. +

+ +

Abstract

+ +This is a proposal to allow procedure calls that evaluate +to the "value of a location" to be used to set +the value of the location, when used as the first +operand of set!. +For example: +
+(set! (car x) (car y))
+
+becomes equivalent to +
+(set-car! x (car y))
+
+ +

+ Many programming languages have the concept of an lvalue. +that is an "expression" that "evaluates" to a location, and +which can appear on the left-hand-side of an assignment. +Common Lisp has a related concept of "generalized variables" +which can be used in setf and some other special forms. +However, the Common Lisp concept is based on the idea of +compile-time recognition of special "location-producing" functions; +this does not seem to be in the "spirit of Scheme". +

+This SRFI proposes an extension of set! +so that it provides similar functionality as Common Lisp's setf, +except that the updater is associated with a procedure value, +rather than a name. + +

Rationale

+ +There is ample precedent for general "lvalues" on the +left-hand side of an assignment. This includes most statically +typed languages, and many dynamically typed languages (including APL +and Common Lisp). That suggests this is a natural idiom for people. +One reason may be that there are fewer procedure names to learn. +Another is that it becomes visually clearer which expression is the new value, +and which are parameters. Also, the visual consistency between +an expression evaluated for its value and one evaluated to yield +a location seems natural to people. +

+For most languages, the set of lvalue-producing operators is limited +(typically array indexing and field selection). Some languages have +general lvalues as first class values. For example Algol 68 has +expressions that have reference type. However, this is made convenient +by using automatic dereferencing coercions, which would not work for +a dynamically typed language like Scheme. ML goes further: All +mutable variables are first-class "cells", and accessing the +contents of a cell requires an explicit operator. This is also not +compatible with Scheme. Instead we need to stick to the model +where using a variable in most contexts means using its value, +but refering to a variable in certain lvalue contexts (lhs of +assignment) refers to its actual location. Sticking to this model +for general "lvalue expressions" in set! means +that "evaluation" must be done differently from normal +evaluation when in an "lvalue context". That is what this proposal does. +

+This is a controversial issue. This srfi does not wish to imply that +all Scheme implementations should support this feature; it only +requests that implementations that do implement +generalized set! should be compatible with this srfi. + +

Specification

+ +The special form set! is extended so the first operand +can be a procedure application, and not just a variable. +The procedure is typically one that extracts a component from +some data structure. Informally, when the procedure is called +in the first operand of set!, it causes the corresponding +component to be replaced by the second operand. +For example, +
+(set (vector-ref x i) v)
+
+would be equivalent to: +
+(vector-set! x i v)
+
+

+Each procedure that may be used as the first operand to set! +must have a corresponding "setter" procedure. +The builtin procedure setter takes a procedure and returns the +corresponding setter procedure. +

+We define: +

+(set! (proc arg ...) value)
+
+as: +
+((setter proc) arg ... value)
+
+

Note: +This definition matches +the existing Scheme convention for setter procedures, where +the new value is given last. For example we can define +(setter car) to be set-car!. +An alternative definition would be: +

+((setter proc) value arg ...) ;; Not the actual definition.
+
+This definition would work better when you consider +procedures that take a variable number of arguments. +This is because it is straight-forward to add one extra +initial fixed argument, but if you add an extra fixed +argument to the end of an argument list that has +a "rest" parameter, then things get more messy. +However, consistency with the existing new-value-last convention +seems more valuable. + +

Standard setters

+ +The following standard procedures have pre-defined setters: +
+(set! (car x) v) == (set-car! x v)
+(set! (cdr x) v) == (set-cdr! x v)
+(set! (caar x) v) == (set-car! (car x) v)
+(set! (cadr x) v) == (set-car! (cdr x) v)
+....
+(set! (caXXr x) v) == (set-car! (cXXr x) v)
+(set! (cdXXr x) v) == (set-cdr! (cXXr x) v)
+(set! (string-ref x i) v) == (string-set! x i v)
+(set! (vector-ref x i) v) == (vector-set! x i v)
+
+ + + +

Setting setters; properties

+ +A setter procedure is a special case of the concept of procedures having +associated properties. Other properties might include +the procedures's name or usage documentation. +As a hypothetical example (i.e. not part of this SRFI), +we can use the Common Lisp documentation function, +where for example: +
+(documentation sqrt)
+
+returns the "documentation string" (if defined) for sqrt. +Such properties should also be settable using generalized set!. +For example: +
+(set! (documentation sqrt) "Calculates the square root.")
+
+ +

+This SRFI does +not propose a general "procedure properties" feature, but it +should be compatible with it. It does specify the special case +for the setter property. This is defined such that: +

+(set! (setter proc) setter)
+
+sets the setter procedure associated with proc +to setter. +For example, we can assume +
+(set! (setter car) set-car!)
+
+has been executed by the Scheme prologue. + +

Efficiency Issues

+ +If (set! (foo ..) ...) is to be the preferred idiom, +we want to make ((setter foo) ...) as efficient +as (set-foo! ...). +This is only possible when the compiler knows both what function +the symbol foo is bound to, and the setter +associated with that function. Scheme (as opposed to Common Lisp) +does not say anything about a compiler or what it can inline, +so we cannot say much here. A compiler that does whole-program +analysis can safely inline calls using a variable bound unchangably +to a known procedure; it can make the same deduction if the +procedure's setter is set. If separate compilation is supported, +then a compiler cannot safely make such deductions for either +plain calls or setter calls, without extra information, such as +a module system, compiler switches, or other non-standard declarations. +Thus my belief is that this srfi does not inherently make efficient +compilation more difficult. However, the next section does define +a way to inherently tie a setter to a procedure, which does reduce +the problem of inlining generalized set! to the +standard inlining problem. + +

getter-with-setter

+

The function getter-with-setter can be used +to define a procedure with associated properties. +Specifically: +

+(getter-with-setter getter setter)
+
+This evaluates to a new anonymous procedure which when +applied invokes getter, and whose setter is setter. +It is an error for a program to subsequently try to modify +the setter of the resulting compound. +

+For example, we could define: +

+(define car (getter-with-setter %primitive-car %primitive-set-car!))
+(define set-car! %primitive-set-car!)
+
+The advantage of this approach that whenever a compiler can inline +car, it can also inline (setter car). + +

Implementation

+ +Here is a sample implementation +for Twobit. +

Here is a sample definition of getter-with-setter: +

+(define (getter-with-setter get set)
+  (let ((proc (lambda args (apply get args))))
+    (set! (setter proc) set)
+    proc))
+
+ +

Copyright

+

Copyright (C) Per Bothner (1999, 2000). All Rights Reserved.

+ +

+Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: +

+

+The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. +

+

+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +

+ +
+
Editor: Mike Sperber
+ + +Last modified: Mon Jul 24 12:00:06 MST 2000 + + + diff --git a/doc/srfi-std/srfi-19.html b/doc/srfi-std/srfi-19.html new file mode 100644 index 0000000000..ddd656c152 --- /dev/null +++ b/doc/srfi-std/srfi-19.html @@ -0,0 +1,662 @@ + + + + SRFI 19: Time Data Types and Procedures + + + + + + + + + + +

Title

+ +SRFI 19: Time Data Types and Procedures + +

Author

+ +Will Fitzgerald + +

Status

+ +

This SRFI is currently in ``final'' status. To see an explanation of +each status that a SRFI can hold, see here. You can +access the discussion via +the archive of the mailing list. + + +

+ +

Abstract

+ +Points in time are represented a the number of seconds (with +nanosecond precision) since "the epoch," a zero point in time. Several +standard variants are defined, including UTC (universal coordinated +time), TAI (international atomic time), and monotonic time. A point in time can also be +represented as a Julian Day or Modified Julian Day number. Time +durations, including time spent in a process or thread, are defined. Conversion +routines are provided. The procedure CURRENT-TIME queries the current +time in a specified variant, with a system-dependent +resolution. Procedures for time arithmetic and time comparisons are +also provided. + +

A date is a representation of a point in time in the Gregorian +calendar, a 24 hour clock (with nanosecond precision) and a +time zone offset from UTC. Procedures for +converting between time and dates are provided, as well as for reading +and writing string representations of dates. + +

Issues

[None currently] + + +

Rationale

+ +R5RS Scheme does not provide standard data types for time. This +SRFI addresses this lack by specifying data types for time and +associated procedures. + + +

Specification

+ +

A Time object, which is distinct from all existing types, +defines a point in time or a time duration in some standard time +system. The standard time systems are: + +

+ +Implementations are required to implement UTC, monotonic time, CPU +time in current process, and time duration. Implementations are +allowed to create extensions (for example, amount of time spent in +garbage collection). + +

A time object consists of three components: +

+ +

A Date object, which is distinct from all existing +types, represents a point in time as represented by the Gregorian +calendar as well as by a time zone. Dates are immutable. A date +consists of the following components: + +

+ +

A Julian Day represents a point in time as a real number +of days since -4714-11-24T12:00:00Z (November 24, -4714 at noon, +UTC). + +

A Modified Julian Day represents a point in time as a +real number of days since 1858-11-17T00:00:00Z (November 17, +1858 at midnight, UTC). + +

Constants

+

The following constants are required: + +

+
time-duration +
Symbol representing Time duration. +
time-monotonic +
Symbol representing monotonic time. +
time-process +
Symbol representing time spent in current process. +
time-tai +
Symbol representing TAI time. +
time-thread +
Symbol representing time spent in current thread. +
time-utc +
Symbol representting UTC time. +
+

Current time and clock resolution

+

The following procedures are required: + +

+
current-date [tz-offset] -> date +
Date corresponding to the current UTC time. +
current-julian-day -> jdn +
Current Julian Day. +
current-modified-julian-day -> mjdn +
Current Modified Julian Day. +
current-time [time-type] -> time +
Current time, of type time-type system, which defaults to TIME-UTC. +
time-resolution [time-type] -> integer +
Clock resolution, in nanoseconds, of the system clock of type type time-type system, which defaults to TIME-UTC. +
+

Time object and accessors

+

The following procedures are required: +

+
make-time type nanosecond second -> time +
Creates a time object. +
time? object -> boolean +
#t if object is a time object, otherwise, #f. +
time-type time -> time-type +
Time type. +
time-nanosecond time -> integer +
Time nanosecond. +
time-second time -> integer +
Time second. +
set-time-type! time time-type +
Changes time type. Note: This changes the semantics of the time object. To convert a time to another + system of representation, use one of the conversion procedures. +
set-time-nanosecond! time integer +
Changes time nanosecond. +
set-time-second! time integer +
Changes time second. +
copy-time time1 -> time2 +
Creates a new time object, with the same time type, nanosecond, and second as time1. +
+ +

Time comparison procedures

+ +

All of the time comparison procedures require the time objects to +be of the same type. It is an error to use these procedures on time +objects of different types. For the point-in-time measurements (e.g., +TIME-TAI and TIME-UTC), the semantics are +described in plain text. For durations, (e.g., +TIME-DURATION, TIME-CPU, the semantics are +described in parentheses. + +

The following procedures are required: +

+
time<=? time1 time2 -> boolean +
#t if time1 is before or at (less than or equal to) time2, #f otherwise. +
time<? time1 time2 -> boolean +
#t if time1 is before (less than) time2, #f otherwise. +
time=? time1 time2 -> boolean +
#t if time1 at (equal) time2, #f otherwise. +
time>=? time1 time2 -> boolean +
#t if time1 is at or after (greater than or equal to) time2, #f otherwise. +
time>? time1 time2 -> boolean +
#t if time1 is after (greater than) time2, #f otherwise. +
+ +

Time arithmetic procedures

+ +

The following procedures are required. +

+
time-difference time1 time2 -> time-duration +
The TIME-DURATION between time1 and time2. It is an error if time1 and time2 are of different time types. A new time object is created. +
time-difference! time1 time2 -> time-duration +
The TIME-DURATION between time1 and time2. It is an error if time1 and time2 are of different time types. Time1 may be used to create the resulting TIME-DURATION object. +
add-duration time1 time-duration -> time +
The time resulting from adding time-duration to time1, which is a time object of the same time type as time1. A new time object is created. +
add-duration! time1 time-duration -> time +
The time resulting from adding time-duration to time1, which is a time object of the same time type as time1. Time1 may used to create the resulting time object. +
subtract-duration time1 time-duration -> time +
The time resulting from subtracting time-duration to time1, which is a time object of the same time type as time1. A new time object is created. +
subtract-duration! time1 time-duration -> time +
The time resulting from subtracting time-duration to time1, which is a time object of the same time type as time1. Time1 may used to create the resulting time object. +
+ +

Date object and accessors

+ +

Date objects are immutable once created. The following procedures are required. +

+ +
make-date nanosecond second minute hour day month year zone-offset -> date +
Creates a date object. +
date? date -> boolean +
#t if object is a time object, otherwise, #f. +
date-nanosecond date -> integer +
Date nanosecond. +
date-second date -> integer +
Date second. +
date-minute date -> integer +
Date minute. +
date-hour date -> integer +
Date hour. +
date-day date -> integer +
Date day. +
date-month date -> integer +
Date month. +
date-year date -> integer +
Date year. +
date-zone-offset date -> integer +
Date time zone offset. +
date-year-day date -> integer +
The ordinal day of the year of this date. January 1 is 1, etc. +
date-week-day date -> integer +
The day of the week of this date, where Sunday=0, Monday=1, etc. +
date-week-number date day-of-week-starting-week -> integer +
The ordinal week of the year which holds this date, ignoring a first partial week. 'Day-of-week-starting-week' is the integer corresponding to the day of the week which is to be considered the first day of the week (Sunday=0, Monday=1, etc.). +
+ +

Time/Date/Julian Day/Modified Julian Day Converters

+ +The following conversion procedures are required. +
+
date->julian-day date -> jd +
Convert date to Julian Day. +
date->modified-julian-day date -> mjd +
Convert date to Modified Julian Day. +
date->time-monotonic date -> time-monotonic +
Convert date to monotonic time. +
date->time-tai date -> time-tai +
Convert date to TAI time. +
date->time-utc date -> time-utc +
Convert date to UTC time. +
julian-day->date jd [tz-offset] -> date +
Convert Julian Day to date, , using time zone offset, which defaults to the local time zone. +
julian-day->time-monotonic jd -> time-monotonic +
Convert Julian Day to monotonic time. +
julian-day->time-tai jd -> time-tai +
Convert Julian Day to TAI time. +
julian-day->time-utc jd -> time-utc +
Convert Julian Day to UTC time. +
modified-julian-day->date mjd [tz-offset] -> date +
Convert Modified Julian Day to date, using time zone offset, which defaults to the local time zone. +
modified-julian-day->time-monotonic mjd -> time-monotonic +
Convert Modified Julian Day to monotonic time. +
modified-julian-day->time-tai mjd -> time-tai +
Convert Modified Julian Day to TAI time. +
modified-julian-day->time-utc mjd -> time-utc +
Convert Modified Julian Day to UTC time. +
time-monotonic->date time-monotonic [tz-offset] -> date +
Convert monotonic time to date, using time zone offset, which defaults to the local time zone. +
time-monotonic->julian-day time-monotonic -> jd +
Convert monotonic time to Julian Day. +
time-monotonic->modified-julian-day time-monotonic -> mjd +
Convert monotonic time to Modified Julian Day. +
time-monotonic->time-tai time-monotonic -> time-tai +
Convert monotonic time to TAI time. +
time-monotonic->time-tai! time-monotonic -> time-tai +
Convert monotonic time to TAI time. The time structure may be reused. +
time-monotonic->time-utc time-monotonic -> time-utc +
Convert monotonic time to UTC time. +
time-monotonic->time-utc! time-monotonic -> time-utc +
Convert monotonic time to UTC time. The time structure may be reused. +
time-tai->date time-tai [tz-offset] -> date +
Convert TAI time to date, using time zone offset, which defaults to the local time zone. +
time-tai->julian-day time-tai -> jd +
Convert TAI time to Julian Day. +
time-tai->modified-julian-day time-tai -> mjd +
Convert TAI time to Modified Julian Day. +
time-tai->time-monotonic time-tai -> time-monotonic +
Convert TAI time to monotonic time. +
time-tai->time-monotonic! time-tai -> time-monotonic +
Convert TAI time to monotonic time. The time structure may be reused. +
time-tai->time-utc time-tai -> time-utc +
Convert TAI time to monotonic time. +
time-tai->time-utc! time-tai -> time-utc +
Convert TAI time to monotonic time. The time structure may be reused. +
time-utc->date time-utc [tz-offset] -> time-utc +
Convert UTC time to date, using time zone offset, which defaults to the local time zone. +
time-utc->julian-day time-utc -> jd +
Convert UTC time to Julian Day +
time-utc->modified-julian-day time-utc -> mjd +
Convert UTC time to Modified Julian Day. +
time-utc->time-monotonic time-utc -> time-monotonic +
Convert UTC time to monotonic time. +
time-utc->time-monotonic! time-utc -> time-monotonic +
Convert UTC time to monotonic time. The time structure may be reused. +
time-utc->time-tai time-utc -> time-tai +
Convert UTC time to TAI time. +
time-utc->time-tai! time-utc -> time-tai +
Convert UTC time to TAI time. The time structure may be reused. +
+ +

Date to String/String to Date Converters

+ +These procedures provide conversion to and from strings. They are required. The specification below describes a 'locale;' the +specification of locales is beyond this SRFI. + +
+
date->string date [format-string] -> string +
Converts a date to a string, using the format string. The format string is copied as is; except +escape characters (indicated by the tilde) are replaced with specfic conversions. Table 1 lists the required conversion specifiers; +implementations are free to extend this list. +
string->date input-string template-string -> date +
Converts an input string to a date, using the template string. The input string must match the template +sting as is; except escape characters (indicate by the tilde) indicate special converters which (1) move to the next character in the input string fulfilling a criterion; (2) read a value, and (3) act on this value in some way. Table 2 lists the required +converters; implementations are free to extend this list. +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

ChConversion

~~a literal ~
~alocale's abbreviated weekday name (Sun...Sat)
~Alocale's full weekday name (Sunday...Saturday)
~blocale's abbreviate month name (Jan...Dec)
~Blocale's full month day (January...December)
~clocale's date and time (e.g., "Fri Jul 14 20:28:42-0400 2000")
~dday of month, zero padded (01...31)
~Ddate (mm/dd/yy)
~eday of month, blank padded ( 1...31)
~fseconds+fractional seconds, using locale's decimal separator (e.g. 5.2).
~hsame as ~b
~Hhour, zero padded, 24-hour clock (00...23)
~Ihour, zero padded, 12-hour clock (01...12)
~jday of year, zero padded
~khour, blank padded, 24-hour clock (00...23)
~lhour, blank padded, 12-hour clock (01...12)
~mmonth, zero padded (01...12)
~Mminute, zero padded (00...59)
~nnew line
~Nnanosecond, zero padded
~plocale's AM or PM
~rtime, 12 hour clock, same as "~I:~M:~S ~p"
~snumber of full seconds since "the epoch" (in UTC)
~Ssecond, zero padded (00...60)
~thorizontal tab
~Ttime, 24 hour clock, same as "~H:~M:~S"
~Uweek number of year with Sunday as first day of week (00...53)
~Vweek number of year with Monday as first day of week (01...52)
~w day of week (0...6)
~Wweek number of year with Monday as first day of week (01...52)
~xweek number of year with Monday as first day of week (00...53)
~Xlocale's date representation, for example: "07/31/00"
~ylast two digits of year (00...99)
~Yyear
~ztime zone in RFC-822 style
~Zsymbol time zone (not-implemented)
~1ISO-8601 year-month-day format
~2ISO-8601 hour-minute-second-timezone format
~3ISO-8601 hour-minute-second format
~4ISO-8601 year-month-day-hour-minute-second-timezone format
~5ISO-8601 year-month-day-hour-minute-second format

Table 1: DATE->STRING conversion specifiers

+ +

+ + + + + + + + + + + + + + + + + + + + + + + +

ChSkip toReadSet

~~anyread literal ~nothing
~achar-alphabetic?abbreviated weekday in localenothing
~Achar-alphabetic?full weekday in localenothing
~bchar-alphabetic?abbreviated month name in localenothing
~Bchar-alphabetic?full month name in localenothing
~dchar-numeric?day of monthdate-day
~eanyday of month, blank paddeddate-day
~hchar-alphabetic?same as ~bnothing
~Hchar-numeric?hourdate-hour
~kanyhour, blank paddeddate-hour
~mchar-numeric?monthdate-month
~Mchar-numeric?minutedate-minute
~Schar-numeric?seconddate-second
~yany2-digit yeardate-year within 50 years
~Ychar-numeric?yeardate-year
~zanytime zonedate-zone-offset

Table 2: STRING->DATE conversion specifiers

+ +

Implementation

+ +This SRFI cannot be written in completely standard Scheme. In +particular, there must be some system-independent method of finding +the values for CURRENT-TIME. The GNU C function, +gettimeofday might prove useful to implementors. + +

The difference between TAI and UTC is not determinate, and +implementations must provide some method for getting TAI. A procedure +is provided in the accompany implmentation for reading the leap second +table provided by the Time Service of the US Naval Observatory +(available at ftp://maia.usno.navy.mil/ser7/tai-utc.dat). + +

The accompanying implementation assumes SRFI 6 Basic String Ports. +The accompanying implementation also assumes an error +procedure. The accompanying implementation also assumes SRFI 8 RECEIVE: Binding to +multiple values. which is easy to implement with the following +syntax: + + +

+(define-syntax receive
+  (syntax-rules ()
+    ((receive formals expression body ...)
+     (call-with-values (lambda () expression)
+                       (lambda formals body ...)))))
+
+ + +

Note that it contains TAI-UTC.DAT reader. + +

The accompanying +implementation is written in MzScheme. MzScheme provides the procedure +current-seconds, which returns the number of seconds (UTC) +since 1970-01-01T00:00:00Z+00:00, and +current-milliseconds, which is a monotonic time +clock. Combining these provides an implementation of +(current-time time-utc). Monontonic time, in this +implementation, is the same as TAI time; differences between TAI and +UTC are resolved through a leap second table. According to the +International Earth Rotation Service, there will be no leap second in +December, 2000. Thus, the leap second table is guaranteed to be +correct through June, 2000. + +

Also, MzScheme (as of version 102, I believe) provides a method for +returning the current time zone offset, via its SECONDS->DATE and +CURRENT-DATE procedures. + +

MzScheme's DEFINE-STRUCT was used to define the time +and date objects. SRFI 9, Defining Record +Types, could be used instead. + +

Procedures meant to be used internally have names beginning with +TM:. Locale-related constants and procedures have +locale in their name; if a 'locale' SRFI is ever written, +it might be good to use that code instead. + +

From this, the rest of the implementation was built. + +

There is also a test suite. + +

Acknowledgements

+ +Claus +Tøndering's Frequently Asked Questions about calendars was a very +useful resource. The implementation of Julian Day, Modified Julian +Day, and Year Day comes from his FAQ. Markus Kuhn has a useful +description of the ISO Standard 8601 for +Date/Time notation; The W3 Consortium also has a useful Note. + +

Mike Sperber, Marc Feely, Dave Mason, and "Prfnoff" all made useful +comments on previous versions of this draft. Thanks to Shriram +Krishnamurthi for his editing help. + +

The DATE->STRING procedure uses a format string, based on +GNU C's date procedure, as well as scsh's +FORMAT-DATE procedure. + + +

Copyright

+ +

Copyright (C) Neodesic Corporation (2000). All Rights Reserved.

+ +

+Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: +

+

+The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. +

+

+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +

+ +
+ +
Editor: Shriram +Krishnamurthi
+Last +modified by the author:
(display (date->string (current-date 0) +"~4")): 2004-03-15T02:21:15Z + diff --git a/doc/srfi-std/srfi-2.html b/doc/srfi-std/srfi-2.html new file mode 100644 index 0000000000..18b6ba769f --- /dev/null +++ b/doc/srfi-std/srfi-2.html @@ -0,0 +1,195 @@ + + + SRFI 2: AND-LET*: an AND with local bindings, a guarded LET* special form + + +

Title

+ +SRFI-2: AND-LET*: an AND with local bindings, a guarded LET* special form + +

Author

+ +Oleg Kiselyov + +

Status

+This SRFI is currently in ``final'' status. To see an explanation of each status that a SRFI can hold, see here. +You can access the discussion on this SRFI via the archive of the mailing list. +

+ +

Abstract

+ +

Like an ordinary AND, an AND-LET* special form evaluates its arguments -- +expressions -- one after another in order, till the first one that +yields #f. Unlike AND, however, a non-#f result of one expression can +be bound to a fresh variable and used in the subsequent expressions. +AND-LET* is a cross-breed between LET* and AND. + +

Rationale

+ +

In case of an ordinary AND formed of proper boolean expressions:
+(AND E1 E2 ...)
+expression E2, if it gets to be evaluated, knows that E1 has returned +non-#f. Moreover, E2 knows exactly what the result of E1 was -- #t -- +which E2 can use to its advantage. If E1 however is an extended +boolean expression, E2 can no longer tell which particular non-#f +value E1 has returned. Chances are it took a lot of work to evaluate +E1, and the produced result (a number, a vector, a string, etc) may be +of value to E2. Alas, the AND form merely checks that the result is +not an #f, and throws it away. If E2 needs it, it has to compute that +value anew. This proposed AND-LET* special form lets constituent +expressions get hold of the results of already evaluated expressions, +without re-doing their work. + +

AND-LET* can be thought of as a combination of LET* and AND, or a +generalization of COND's send operator =>. An AND-LET* form can also be +considered a sequence of guarded expressions. In a regular program, +forms may produce results, bind them to variables and let other forms +use these results. AND-LET* differs in that it checks to make sure that +every produced result "makes sense" (that is, not an #f). The first +"failure" triggers the guard and aborts the rest of the sequence +(which presumably would not make any sense to execute anyway). + +Examples: +

+(AND-LET* ((my-list (compute-list)) ((not (null? my-list))))
+          (do-something my-list))
+
+(define (look-up key alist)
+  (and-let* ((x (assq key alist))) (cdr x)))
+
+(or
+  (and-let* ((c (read-char))
+    ((not (eof-object? c))))
+    (string-set! some-str i c)  
+    (set! i (+ 1 i)))
+  (begin (do-process-eof)))
+
+			; A more realistic example
+                        ; Parse the 'timestamp' ::= 'token1' 'token2'
+                        ;   token1 ::= 'YY' 'MM' 'J'
+                        ;   token2 ::= 'GG' 'gg' "/"
+(define (parse-full-timestamp token1 token2)
+  (AND-LET* (((= 5 (string-length token1)))
+             ((= 5 (string-length token2)))
+             (timestamp
+               (OS:string->time "%m/%d/%y %H:%M"
+                 (string
+                   (string-ref token1 2) (string-ref token1 3) #\/
+                   (string-ref token1 0) (string-ref token1 1) #\/
+                   (case (string-ref token1 4)
+                     ((#\8 #\9) #\9) (else #\0))
+                   (string-ref token1 4) #\space
+                   (string-ref token2 0) (string-ref token2 1) #\:
+                   (string-ref token2 2) (string-ref token2 3))))
+             ((positive? timestamp)))
+           timestamp))
+
+ +

+AND-LET* is also similar to an "anaphoric AND" LISP macro [Rob Warnock, +comp.lang.scheme, 26 Feb 1998 09:06:43 GMT, Message-ID: +6d3bb3$3804h@fido.asd.sgi.com]. AND-LET* allows however more than one +intermediate result, each of which continues to be bound through the +rest of the form. + + +

Specification

+ +

Syntax and Informal Semantics

+ +
+AND-LET* (CLAWS) BODY
+
+CLAWS ::= '() | (cons CLAW CLAWS)
+CLAW  ::=  (VARIABLE EXPRESSION) | (EXPRESSION) |
+           BOUND-VARIABLE
+
+ + + +

Formal (Denotational) Semantics

+ +
+eval[ (AND-LET* (CLAW1 ...) BODY), env] =
+   eval_claw[ CLAW1, env ] andalso
+   eval[ (AND-LET* ( ...) BODY), ext_claw_env[CLAW1, env]]
+
+eval[ (AND-LET* (CLAW) ), env] = eval_claw[ CLAW, env ]
+eval[ (AND-LET* () FORM1 ...), env] = eval[ (BEGIN FORM1 ...), env ]
+eval[ (AND-LET* () ), env] = #t
+
+eval_claw[ BOUND-VARIABLE, env ] =
+   eval[ BOUND-VARIABLE, env ]
+eval_claw[ (EXPRESSION), env ] =
+   eval[ EXPRESSION, env ]
+eval_claw[ (VARIABLE EXPRESSION), env ] =
+   eval[ EXPRESSION, env ]
+
+ext_claw_env[ BOUND-VARIABLE, env ] = env
+ext_claw_env[ (EXPRESSION), env ] =
+   env-after-eval[ EXPRESSION, env ]
+ext_claw_env[ (VARIABLE EXPRESSION), env ] =
+   extend-env[ env-after-eval[ EXPRESSION, env ],
+              VARIABLE boundto eval[ EXPRESSION, env ]]
+
+ +

Implementation

+ +

The full implementation plus the validation code are available here (which is a copy of + +http://pobox.com/~oleg/ftp/Scheme/vland.scm). + +

This is an implementation of AND-LET* as a (Gambit) low-level macro that +re-writes AND-LET* as a "tree" of AND and LET forms. A validation code is +also presented, which verifies not only that everything works as +expected, but also that AND-LET* finds syntax errors where expected. + +

Copyright

+Copyright (C) Oleg Kiselyov (1998). All Rights Reserved. +

+Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: +

+

+The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. +

+

+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +

+ +
+
Editor: Dave Mason
+ +

+ +Last modified: Wed Feb 6 17:21:57 MET 2002 + + + + + diff --git a/doc/srfi-std/srfi-23.html b/doc/srfi-std/srfi-23.html new file mode 100644 index 0000000000..174eda493c --- /dev/null +++ b/doc/srfi-std/srfi-23.html @@ -0,0 +1,212 @@ + + + + SRFI 23: Error reporting mechanism + + + + +

Title

+ +SRFI 23: Error reporting mechanism + +

Author

+ +Stephan Houben + +

Status

+ +This SRFI is currently in ``final'' status. To see an explanation of +each status that a SRFI can hold, see here. +You can access +the discussion via + +the archive of the mailing list. +

+

+

+ +

Abstract

+ +A mechanism is proposed to allow Scheme code to report errors and abort +execution. +The proposed mechanism is already implemented in several Scheme systems +and can be implemented, albeit imperfectly, in any R5RS conforming Scheme. + +

Rationale

+ +

+R5RS Scheme requires certain operations to signal an error when they fail. +"Signalling an error" means that implementations must detect and report +the error. +Moreover, R5RS encourages, but not requires, +implementations to signal an error in many more circumstances. + +

+However, there is no direct way for the Scheme application programmer to report +an error that occured in his or her own application. +This means that Scheme procedures created by applications or libraries are +in this respect not on equal footing with procedures provided by the +Scheme system. + +

+Many Scheme systems already provide a mechanism that allows application code +to report an error. At least the following implementations support +such a mechanism: Bigloo, Guile, MIT Scheme, PLT Scheme, RScheme, Scsh, SCM, +all implementations supported by SLIB. +Of these implementations, the following have an error mechanism compatible +with this SRFI: Guile, MIT Scheme, PLT Scheme, RScheme, Scsh. +The implementation in SLIB has a different name than the +one proposed in this SRFI. + +

+To summarise, many implementations already have the error reporting +mechanism described in this SRFI and others are easily made compatible +with this SRFI. This shows that the proposed mechanism is considered useful +and that it is easy to implement in most major implementations. + +

Specification

+ +

+The following procedure should be provided: +

+(error <reason> [<arg1> [<arg2> ...]]) +

+The argument <reason> should be a string. +The procedure error will signal an error, +as described in R5RS, and it will report the message <reason> +and the objects <arg1>, <arg2>, .... +What exactly constitutes "signalling" and "reporting" is not prescribed, because of the large variation in Scheme systems. So it is left to the implementor +to do something reasonable. To that end, a few examples of possible behaviour +are given. +

    +
  1. + Display <reason> and <arg1>... on the screen and terminate + the Scheme program. (This might be suitable for a Scheme system + implemented as a batch compiler.) +
  2. + Display <reason> and <arg1>... on the screen and + go back to the read-evaluate-print loop. (This might be suitable for + an interactive implementation). +
  3. + In the case of a multi-threaded system: terminate the current + thread, but do not terminate the other threads. Possibly make the + arguments to error available to other threads in some + way. See the thread-join! mechanism in SRFI-18 on + how this could be done. +
  4. + Package <reason> and <arg1>... up into an error object + and pass this error object to an exception handler. The default + exception handler then might do something as described in points 1 to 3. +
  5. + In the case of a Scheme system that runs completely unattended + and that has no way to notify a human, the only reasonable + course of action might be to do nothing at all. However, this should + be considered a last resort. Clearly, if all implementors would choose + this strategy, this SRFI would not be very useful. +
+An implementation might report more information than just +<reason> and <arg1>... . For instance, it might report the procedure name in which +the error occured or even print a stack trace. +However, this will require additional support in the Scheme implementation. + + +

Why error is a procedure

+ +It is conceivable to allow error to be a special form, +such as a macro, rather than a procedure. This might make providing +information such as the source code location easier. This possibility +has been considered, but rejected, for two reasons. +
    +
  1. + Since error accepts a variable number of arguments, + it could occasionally be useful to use apply to call + error. However, this is not possible if error + was allowed to be a special form. +
  2. + Since error is currently a procedure in all Scheme + implementations mentioned above, it doesn't seem all that + worthwhile to allow it to be a special form. +
+ +

Implementation

+ +An implementation that works in almost any R5RS Scheme is the following: +
+  (define (error reason . args)
+      (display "Error: ")
+      (display reason)
+      (for-each (lambda (arg) 
+                  (display " ")
+		  (write arg))
+		args)
+      (newline)
+      (scheme-report-environment -1))  ;; we hope that this will signal an error
+
+This implementation has a flaw, +namely, in many implementations this +will actually print 2 messages. +
    +
  1. The message message, followed by objs, and +
  2. A message about scheme-report-environment getting an invalid argument. +
+This might be confusing to the user. + +

+The SLIB procedure slib:error works like the error +procedure described in this document. +Thus, when SLIB is loaded, error can be defined as: +

+  (define error slib:error)
+
+ +

+If SRFI 18 is supported, it is allowed +(but not required) to implement error in +terms of the exception mechanism of SRFI 18. +

+  (define (error reason . args)
+    (raise (make-error-exception reason args)))
+
+Here, make-error-exception is implementation dependent. + +

Copyright

+

Copyright (C) Stephan Houben (2001). All Rights Reserved.

+ +

+Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: +

+

+The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. +

+

+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +

+ +
+
Editor: Mike Sperber
+ + +Last modified: Mon Feb 4 14:46:29 MET 2002 + + + diff --git a/doc/srfi-std/srfi-25.html b/doc/srfi-std/srfi-25.html new file mode 100644 index 0000000000..4d81d03530 --- /dev/null +++ b/doc/srfi-std/srfi-25.html @@ -0,0 +1,531 @@ + + + + SRFI 25: Multi-dimensional Array Primitives + + + + +

Title

+ +SRFI 25: Multi-dimensional Array Primitives + +

Author

+ +Jussi Piitulainen + +

Status

+ +This SRFI is currently in ``final'' status. To see an explanation of +each status that a SRFI can hold, see here. You can access +the discussion via + +the archive of the mailing list. +

+

+

+ +

Abstract

+ +

+A core set of procedures for creating and manipulating heterogeneous +multidimensional arrays is proposed. The design is consistent with the +rest of Scheme and independent of other container data types. It +provides easy sharing of parts of an array as other arrays without +copying, encouraging a declarative style of programming. +

+ +

+The specification is based on an original contribution by Alan Bawden +in 1993. +

+ +

Rationale

+ +

+The proposed arrays encourage a natural declarative programming +style. They allow sharing of most any rectangular part of an array +through an affine index mapping, without copying. But imperative style +is equally natural. +

+ +

+The design is consistent with the two indexed data structures of +Scheme: vectors and strings. The design makes arrays a self-contained +type. These statements are illustrated in the following paragraphs. +

+ +

+First, in the one-dimensional case, the arguments of the following +relevant calls match exactly. +

+ +
+    (vector-set! v k o)
+    (string-set! s k c)
+    (array-set! a k o)
+
+ +

+Likewise, make-array matches make-vector and +make-string. An analogue to vector, +string and list is provided, alleviating the +lack of an external representation. Index bounds are specified as for +substring, lower bound included and upper bound excluded. +

+ +

+Array shapes are specified as arrays. These can be made with a special +procedure shape that does not have a shape argument. An +array does not retain a dependence to the shape array. For example, +mutation of a shape array is allowed. +

+ +

+Index mappings return multiple values as multiple values. +

+ +

+Array dimensions can begin at any index. In particular, the choice +between 0 and 1 is left to the user. +(Shapes and index objects are zero based, though.) +

+ +

+The ability to pack an index sequence in a vector is useful for +implementing higher level operations. (The ability to pack it in a +one-dimensional array lets one use, say, a row of a matrix as an +index.) +

+ +

+It is not required that vectors not be arrays. It is not required that +they be, either. +

+ +

Specification

+ +

+Arrays are heterogeneous data structures whose elements are indexed by +integer sequences of fixed length. The length of a valid index +sequence is the rank or the number of dimensions +of an array. The shape of an array consists of bounds for +each index. +

+ +

+The lower bound b and the upper bound e of a +dimension are exact integers with +(<= b e). A valid +index along the dimension is an exact integer k that +satisfies both +(<= b k) and +(< k e). The length of +the array along the dimension is the difference +(- e b). The +size of an array is the product of the lengths of its +dimensions. +

+ +

+A shape is specified as an even number of exact integers. These are +alternately the lower and upper bounds for the dimensions of an array. +

+ +

+The following ten procedures should be implemented. +

+ +

+(array? obj)
+ +Returns #t if obj is an array, otherwise +returns #f. +

+ +

+Note: there is no reasonable way to implement this procedure +accurately in R5RS; SRFI +9 (Defining Record Types) specifies a way, and many Scheme +implementations provide something similar. +

+ +

+(make-array shape)
+(make-array shape obj)
+ +Returns a newly allocated array whose shape is given by +shape. If obj is provided, then each element is +initialized to it. Otherwise the initial contents of each element is +unspecified. The array does not retain a dependence to +shape. +

+ +

+(shape bound ...)
+ +Returns a shape. The sequence bound ... must consist of an +even number of exact integers that are pairwise not decreasing. Each +pair gives the lower and upper bound of a dimension. If the shape is +used to specify the dimensions of an array and bound ... is +the sequence b0 e0 ... bk ek ... of n pairs of +bounds, then a valid index to the array is any sequence j0 ... jk +... of n exact integers where each jk +satisfies (<= bk jk) +and (< jk ek). +

+ +

+The shape of a d-dimensional array is a +d × 2 array where the element at +k 0 contains the lower bound for an index along dimension +k and the element at k 1 contains the +corresponding upper bound, where k satisfies +(<= 0 k) and +(< k d). +

+ +

+(array shape obj ...)
+ +Returns a new array whose shape is given by shape and the +initial contents of the elements are obj ... in row major +order. The array does not retain a dependence to shape. +

+ +

+(array-rank array)
+ +Returns the number of dimensions of array. +

+    (array-rank
+       (make-array (shape 1 2 3 4)))
+
+ +

+Returns 2. +

+ +

+(array-start array k)
+ +Returns the lower bound for the index along dimension k. +

+ +

+(array-end array k)
+ +Returns the upper bound for the index along dimension k. +

+ + + +

+(array-ref array k ...)
+(array-ref array index)
+ +Returns the contents of the element of array at index +k .... The sequence k ... must be a valid index +to array. In the second form, index must be +either a vector or a 0-based 1-dimensional array containing k +.... +

+ +
+    (array-ref (array (shape 0 2 0 3)
+                  'uno 'dos 'tres
+                  'cuatro 'cinco 'seis)
+       1 0)
+
+ +

+Returns cuatro. +

+ +
+    (let ((a (array (shape 4 7 1 2) 3 1 4)))
+       (list (array-ref a 4 1)
+             (array-ref a (vector 5 1))
+             (array-ref a (array (shape 0 2)
+                             6 1))))
+
+ +

+Returns (3 1 4). +

+ +

+(array-set! array k ... obj)
+(array-set! array index obj)
+ +Stores obj in the element of array at index +k .... Returns an unspecified value. The sequence k +... must be a valid index to array. In the second +form, index must be either a vector or a 0-based +1-dimensional array containing k .... + +

+    (let ((a (make-array
+                (shape 4 5 4 5 4 5))))
+       (array-set! a 4 4 4 'huuhkaja)
+       (array-ref a 4 4 4))
+
+ +

+Returns huuhkaja. +

+ +

+(share-array array shape proc) +
+ +Returns a new array of shape shape that shares elements of +array through proc. The procedure +proc must implement an affine function that returns indices +of array when given indices of the array returned by +share-array. The array does not retain a dependence to +shape. +

+ +
+    (define i_4
+       (let* ((i (make-array
+                    (shape 0 4 0 4)
+                    0))
+              (d (share-array i
+                    (shape 0 4)
+                    (lambda (k)
+                       (values k k)))))
+          (do ((k 0 (+ k 1)))
+              ((= k 4))
+             (array-set! d k 1))
+          i))
+
+ +

+Note: the affinity requirement for proc means that each +value must be a sum of multiples of the arguments passed to +proc, plus a constant. +

+ +

+Implementation note: arrays have to maintain an internal index mapping +from indices k1 ... kd to a single index into a backing +vector; the composition of this mapping and proc can be +recognised as (+ n0 (* n1 k1) ... (* nd +kd)) by setting each index in turn to 1 +and others to 0, and all to 0 for the +constant term; the composition can then be compiled away, together +with any complexity that the user introduced in their procedure. +

+ +

+This document does not specify any external representation for arrays. +This document does not specify when arrays are equal?. +(Indeed, R5RS equal? will do the wrong thing.) +

+ +

Examples

+ +

+The reference implementation comes with a number of files that +illustrate some ways to use the proposed system (and are very useful +in testing an implementation; that is their origin). +

+ +
    + +
  1. A library arlib.scm that contains, among + several other things, tabulate-array for a more + useful initialization of a new array, an + array-equal?, and a transpose that can + permute the dimensions of an array any which way. + +
  2. A test suite test.scm for array, + and another test suite list.scm for + arlib. + +
  3. A rudimentary display procedure (play array) in play.scm, for playing around with the system. + +
+ +

Implementation

+ +

+A portable reference implementation is provided. It uses a minimal +error reporting mechanism that conforms to SRFI 23 (Error reporting +mechanism). Type disjointness requires support from the host +implementation, such as support for SRFI 9 (Defining Record +Types). All names not defined in this proposal are in the prefix +"array:", which serves as a module system. +

+ +

+You can get source for the reference +implementation as a single file and stop reading. But there are +variations. This single file represents arrays as procedures (so the +type predicate is very approximate); it represents index mapping as +vectors of coefficients; map recognition is not optimised for any +number of dimensions as that would be redundant in this +representation. +

+ +

+The real source comes in too many files. A working installation +consists of the following parts, each in its own file. +

+ +
    + +
  1. a record type definition, either system specific for + type disjointness, or portable as procedures, in a file + as-*.scm, and + +
  2. indexing operations to match the type, in a file + ix-*.scm, and + +
  3. an affine recogniser of one of three types, optimised + up to some number of dimensions, in a file op-*.scm, and + +
  4. the main source file array.scm. + +
+ +

+Affine recognisers are made by a program opt.scm +but one of each type is also available here, optimized for 0, 1, 2 and +3 dimensions. Choose one type: pick a recogniser with matching index +procedures; load as-, ix- and op- and +array.) +

+ +
    + +
  1. In the mbda type representation, index + mappings are procedures that accept an optional argument. The + matching access procedures apply the + mapping to the arguments of array-ref and + array-set!. + +
  2. In the tter type representation, index + mappings are pairs of procedures: one takes exactly the indices, + the other takes indices and an object. The matching access procedures apply the first + procedure to the argumets of array-ref and the + second procedure to the arguments of array-set!. + +
  3. In ctor representation, index mappings + are coefficient vectors. The access + procedures compute the sum of products of coefficients and + indexes in a loop on the list. + +
+ +

+Record implementations are available for +generic Scheme (arrays are not disjoint from procedures), for SRFI 9 (Defining Record Types) +(not tested), and for PLT +Scheme (arrays belong to a struct type). +

+ +

+With the three files from above, the main source +file should work in any Scheme implementation without need of +modification. +

+ +

+Error checking in the implementation may be a tad expensive. The +places where it occurs are cleanly separated from the surrounding +code. (Sharing uses a check that is exponential in the number of +dimensions. It is disabled above a threshold rank.) +

+ +

Acknowledgements

+ +

+The original concept comes from a message to the Usenet newsgroup +comp.lang.scheme by Alan Bawden in 1993. A variant of that +implementation by Richard Kelsey in the Scheme 48 system was also an +influence. Apart from the origins, the main design goal has been +consistency with the core types of Scheme. +

+ +

+Alan Bawden and Mark K. Gardner gave useful comments at an earlier +attempt to make this specification public. (There was at least one +other. Notes have gone missing.) SRFI feedback led to improved +wording, hidden shapes, and two kinds of index objects. +

+ +

+The exact title of the proposal comes from a message titled "a process that might work" by William D. Clinger to the rrrs-authors +mailing list in 1998. That appears to be a part of the past of the SRFI process. +

+ +

Copyright

+

Copyright (C) Jussi Piitulainen (2001). All Rights Reserved.

+ +

+Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: +

+

+The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. +

+

+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +

+ +
+
Editor: Mike Sperber
+ + +Last modified: Tue May 28 18:46:09 MST 2002 + + + diff --git a/doc/srfi-std/srfi-26.html b/doc/srfi-std/srfi-26.html new file mode 100644 index 0000000000..a6cf4878c9 --- /dev/null +++ b/doc/srfi-std/srfi-26.html @@ -0,0 +1,403 @@ + + + + SRFI 26: Notation for Specializing Parameters without Currying + + + + +

Title

+ +SRFI 26: Notation for Specializing Parameters without Currying + +

Author

+ +Sebastian Egner + +

Status

+ +This SRFI is currently in ``final'' status. To see an explanation of +each status that a SRFI can hold, see here. You can access +the discussion via + +the archive of the mailing list. + + + +

Abstract

+ +When programming in functional style, +it is frequently necessary to specialize some of the +parameters of a multi-parameter procedure. +For example, from the binary operation cons +one might want to obtain the unary operation +(lambda (x) (cons 1 x)). +This specialization of parameters is also known as +"partial application", "operator section" or "projection".

+ +The mechanism proposed here allows to write this sort +of specialization in a simple and compact way. +The mechanism is best explained by a few examples:

+ + + + + + + + + + + + +
(cut cons (+ a 1) <>) +is the same as +(lambda (x2) (cons (+ a 1) x2)) +
(cut list 1 <> 3 <> 5) +is the same as +(lambda (x2 x4) (list 1 x2 3 x4 5)) +
(cut list) +is the same as +(lambda () (list)) +
(cut list 1 <> 3 <...>) +is the same as +(lambda (x2 . xs) (apply list 1 x2 3 xs)) +
(cut <> a b) +is the same as +(lambda (f) (f a b)) +

+ +As you see, the macro cut specializes some of the +parameters of its first argument. +The parameters that are to show up as formal +variables of the result are indicated by the symbol <>, +pronouced as "slot". In addition, the symbol <...>, +pronounced as "rest-slot", matches all residual arguments of a variable +argument procedure. +As you can see from the last example above, the first argument can also +be a slot, as one should expect in Scheme.

+ +In addition to cut, there is a variant called cute +(a mnemonic for "cut with evaluated non-slots") which evaluates +the non-slot expressions at the time the procedure is specialized, not at +the time the specialized procedure is called. +For example,

+ + + + +
(cute cons (+ a 1) <>) +is the same as +(let ((a1 (+ a 1))) (lambda (x2) (cons a1 x2))) +

+ +As you see from comparing this example with the first example above, +the cute-variant will evaluate (+ a 1) +once, while the cut-variant will evaluate it during +every invokation of the resulting procedure.

+ +The mechanism proposed in this SRFI allows specializing any subset +of the variables of a procedure. +The result can be of fixed arity or of variable arity. +The mechanism does not allow permutation, omission, duplication +or any other processing of the arguments; +for this it is necessary to write to use a different +mechanism such as lambda. + +

Rationale

+ +A particularly elegant way to deal with specialization is known +as currying (Schönfinkel 1924, Curry 1958). +The idea of currying is to reduce multi-argument functions to +single-argument functions by regarding an n-ary +function as a unary function mapping its first argument into +an (n-1)-ary function (which is curried in turn). +This point of view, apart from its theoretical elegance, +allows an extremely compact notation for specializing the +first argument of a function. +In the first example, one could simply write (cons 1).

+ +Yet, Scheme is not a curried language---the +number of arguments passed to a procedure must match +the number of its parameters at all times. +This allows zero- and variable-arity procedures +but in order to specialize parameters +one usually has to write down a lambda-expression +and invent some irrelevant identifiers for its +formal variables (x in the example). +For this reason, the mechanism proposed in this SRFI +provides a simple and compact notation for specializing +any subset of the parameters of a procedure.

+ +Note: The mechanism proposed here is not currying!

+ +The purpose of the mechanism proposed here is to make the benefits +of currying available within the programming language Scheme. +There are two primary benefits of currying in practice: +Higher-order types are substantially simplified and +there is a simple notation for specializing parameters. +The type aspect is irrelevant as Scheme has latent typing. +The specialization aspect is largly covered with this SRFI.

+ +Here are a few more examples for illustration: + + + + + + + + + + + + +
(map (cut * 2 <>) '(1 2 3 4)) +
(map (cut vector-set! x <> 0) indices) +
(for-each (cut write <> port) exprs) +
(map (cut <> x y z) (list min max)) +
(for-each (cut <>) thunks) +
+ +

Specification

+ + +The formal syntax of a specialized expression, in the style of the +Revised^5 Report on the Algorithmic Language Scheme:

+ + + + + + + + + + + + + + +
<cut-expression> +--> +(cut <slot-or-expr> <slot-or-expr>*) + +
|(cut <slot-or-expr> <slot-or-expr>* <...>) +; with "rest-slot" +
|(cute <slot-or-expr> <slot-or-expr>*) +; evaluate non-slots at specialization time +
|(cute <slot-or-expr> <slot-or-expr>* <...>) +; with "rest-slot" +
<slot-or-expr> +--> +<>; a "slot" +
| <expression>; a "non-slot expression" +

+ +The macro cut transforms a <cut-expression> +into a <lambda expression> with as many formal variables +as there are slots in the list <slot-or-expr>*. +The body of the resulting <lambda expression> calls +the first <slot-or-expr> with arguments from +<slot-or-expr>* in the order they appear. +In case there is a rest-slot symbol, the resulting procedure is also of +variable arity, and the body calls the first <slot-or-expr> +with all arguments provided to the actual call of the specialized procedure.

+ +The macro cute is similar to the macro cut, +except that it first binds new variables to the result of evaluating +the non-slot expressions (in an unspecific order) and then substituting +the variables for the non-slot expressions. +In effect, cut evaluates non-slot expressions at the time +the resulting procedure is called, whereas cute evaluates +the non-slot expressions at the time the procedure is constructed.

+ +

Implementation

+ +The reference implementation defines the two macros +cut and cute using macro +mechanism of R5RS. +It does not use any other SRFI or any library. +The implementation makes use of internal macros to +run through the list of <slot-or-expr;>s. +As macros in R5RS are hygienic and referentially transparent, +the macro mechanism makes sure the names of the newly +introduced formal variables are unique and do not clash. +The template (param ... slot), see +Sect. 7.1.5. of R5RS, +allows to preserve the order of arguments---which would get +reversed otherwise. + +The reference implementation has been written by +Al Petrofsky. It can be found here.

+ +Finally, there is a small collection of +confidence tests. +It checks some special cases of the mechanism defined +in this SRFI and signals an error in case something is wrong. +Passing the tests does not mean a correct implementation. + + +

Design Rationale

+ +

Why not real currying/uncurrying?

+ +It is possible in Scheme to implement a macro turning a multi-argument +procedure into a nesting of single-argument procedures and back. +These operations are usually called "curry" and "uncurry" in +other programming languages. +Yet, Scheme remains an inherently uncurried language and is not +prepared to deal with curried procedures in a convenient way. +Hence, a "by the book" implementation of currying would only be useful +if you apply it in the sequence "curry, specialize some arguments, +and uncurry again"---which is exactly the purpose of the macro +cut specified in this document. +The primary relevance of currying/uncurrying in Scheme is to +teach concepts of combinatory logic.

+ +

Why not a more general mechanism, also allowing permutation, +omission and duplication of arguments?

+ +The reason is that I, the author of this SRFI, consider more general +mechanisms too dangerous to mix them with the mechanism proposed here. +In particular, as soon as parameters are being rearranged it +is usually necessary to be aware of the meaning of the parameters; +unnamed variables can be quite harmful then. +The mechanism proposed here is designed to prevent this. +Please refer to the discussion threads +"OK, how about...," (Alan Bawden), +"is that useful?" (Walter C. Pelissero), and +"l, the ultimate curry that is not curry" (Al Petrofsky).

+ +

Why are the macro called cut/cute and not +[enter your favourite here]?

+ +Well, the original name proposed for this SRFI was curry +which immediately stirred some emotions as it does not what is +commonly known as currying. +Some alternatives have been discussed, such as section, +specialise, specialize, partial-apply, +partial-call, partial-lambda, +_j, _i, $, +&, srfi-26, foobar, +xyz, schoenfinkelize, +curry-which-isnt-curry, tandoori, +and it has also been suggested to pick a five letter symbol uniformly +at random and fix this as a name. +To be fair, not all of these name have been put forward as serious proposals, +some of them were merely to illustrate a point in the discussion. +In addition, I have played with the game of the name quite a bit +and considered other candidates not listed here. +Despite the fact that the discussion list only represents a highly +biased random sample of people's opinion (motivation to post a message +is higher if you disagree, for example) it told me that the SRFI +could potentially benefit from a different name---however impractical +it may be to go for unanimous popularity. +The name cut refers to "operator section", as the +concept is often called in other programming languages, +but I tend to remember it as the acronym for "Curry Upon This" ;-). +The names for the evaluating version of cut that +have been proposed were cut!, cutlet, +cut*, and cute.

+ +

Is it possible to implement the SRFI without macros?

+ +Not really. +As Stephan Houben has pointed out during the discussion (refer to +"Implementing it as a procedure") it is possible to implement the +cute-mechanism as a procedure. +Refer also to Al Petrofsky's posting +"Problems with "curry"'s formal specification" for details. +However, the procedural implementation comes with a slight performance +penalty and it is not possible the implement the cut-mechanism +as a procedure, too. +As both are needed, we rely on macros to implement the SRFI. + +

Why is there another symbol for the rest-slot when lambda-expressions +use the dotted notation for variable length argument lists?

+There are two reasons. +The first one is the existence of a procedural implementation +of a related mechanism (refer to the previous paragraph). +For a procedure, however, it is not possible to have dotted notation. +The second reason is the way the hygienic macro mechanism in R5RS +is defined to deal with dotted notation, as Felix Winkelmann has pointed out. +Refer to the discussion threads +"Improper lists in macros [WAS: none]".

+ +

Why is it impossible to specify when a non-slot is evaluated individually +per non-slot?

+Cut evaluates all non-slots at the time the specialized +procedure is called and cute evaluates all non-slots at +the time the procedure is being specialized. +These are only the two extremes and it is possible to define a +syntax that allows to choose per non-slot. +However, I am convinced that the benefit of the greater flexibility +is not worth the risk of confusion. +If a piece of code really depends on the distinction, it might be +better to make this explicit through let and +lambda.

+ +

Why is (cut if <> 0 1) etc. illegal?

+ +It is specified that a <slot-or-expr> must be +either the slot symbol or an <expression> in the sense +of R5RS, +Section 7.1.3. +As if is no <expression>, +the above case is illegal. +The reason why cut and cute are +restricted in this sense is the difficulty of defining +the meaning of such generalized expressions. +Please refer to the discussion archive for details. + +

Acknowledgements

+ +An important part of this SRFI is based on the contribution +of other people, mostly through the discussion archive. +In particular, the semantics and the design rationale have +been greatly improved in the course of the discussion. +I would like to thank all who have contributed. + +

Copyright

+

Copyright (C) Sebastian Egner (2002). All Rights Reserved.

+ +

+Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: +

+

+The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. +

+

+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +

+ +
+
Editor: Mike Sperber
+
Author: Sebastian Egner
+ + +Last modified: Wed Jun 19 10:54:36 MST 2002 + + + diff --git a/doc/srfi-std/srfi-27.html b/doc/srfi-std/srfi-27.html new file mode 100644 index 0000000000..480f5e1326 --- /dev/null +++ b/doc/srfi-std/srfi-27.html @@ -0,0 +1,821 @@ + + + + SRFI 27: Sources of Random Bits + + + + +

Title

+ +SRFI 27: Sources of Random Bits + +

Author

+ +Sebastian Egner + +

Status

+ +This SRFI is currently in ``final'' status. To see an explanation of +each status that a SRFI can hold, see here. +You can access +previous messages via + +the archive of the mailing list. + + + +

Abstract

+ +This document specifies an interface to sources of random bits, +or "random sources" for brevity. +In particular, there are three different ways to use the interface, +with varying demands on the quality of the source and the +amout of control over the production process: + + +

+ +Once random sources provide the infrastructure to obtain +random bits, these can be used to construct other random deviates. +Most important are floating point numbers of various distributions +and random discrete structures, such as permutations or graphs. +As there is an essentially unlimited number of such objects (with +limited use elsewhere), we do not include them in this SRFI. +In other words, this SRFI is not about making +all sorts of random objects---it is about obtaining random +bits in a portable, flexible, reliable, and efficient way. + +

Rationale

+ +This SRFI defines an interface for sources of random bits +computed by a pseudo random number generator. +The interface provides range-limited integer and real numbers. +It allows accessing the state of the underlying generator. +Moreover, it is possible to obtain a large number of +independent generators and to invoke a mild form of true +randomization.

+ +The design aims at sufficient flexibility to cover the +usage patterns of many applications as diverse as +discrete structures, numerical simulations, and cryptographic protocols. +At the same time, the interface aims at simplicity, +which is important for occasional use. +As there is no "one size fits all" random number generator, +the design necessarily represents some form of compromise +between the needs of the various applications.

+ +Although strictly speaking not part of the specification, +the emphasis of this proposal is on high quality +random numbers and on high performance. +As the state of the art in pseudo random number generators +is still advancing considerably, the choice of method for +the reference implementation should essentially be +considered preliminary. + +

Specification

+ +
+ +
+(random-integer n) -> x +
+ +
+The next integer x in {0, ..., n-1} +obtained from default-random-source. +Subsequent results of this procedure appear to be independent +uniformly distributed over the range {0, ..., n-1}. +The argument n must be a positive integer, +otherwise an error is signalled. +
+ +
+ +
+ +
+(random-real) -> x +
+ +
+The next number 0 < x < 1 obtained from +default-random-source. +Subsequent results of this procedure appear to be +independent uniformly distributed. +The numerical type of the results and the +quantization of the output range depend on the implementation; +refer to random-source-make-reals for details. +
+ +
+ +
+ +
+default-random-source +
+ +
+A random source from which random-integer and +random-real have been derived using +random-source-make-integers and +random-source-make-reals. +Note that an assignment to default-random-source +does not change random or random-real; +it is also strongly recommended not to assign a new value. +
+ +
+
+
+ +
+(make-random-source) -> s +
+ +
+Creates a new random source s. +Implementations may accept additional, optional arguments in +order to create different types of random sources. +A random source created with make-random-source +represents a deterministic stream of random bits generated +by some form of pseudo random number generator. +Each random source obtained as (make-random-source) +generates the same stream of values, unless the state is modified +with one of the procedures below. +
+ +
+ +
+ +
+(random-source? obj) -> bool +
+ +
+Tests if obj is a random source. +Objects of type random source are distinct from all +other types of objects. +
+ +
+ +
+ +
+(random-source-state-ref s) -> state
+(random-source-state-set! s state) +
+ +
+Get and set the current state of a random source s. The +structure of the object state depends on the implementation; +the only portable use of it is as argument to +random-source-state-set!. +It is, however, required that a state possess an external +representation. +
+ +
+ +
+
+(random-source-randomize! s) +
+ +
+Makes an effort to set the state of the random +source s to a truly random state. +The actual quality of this randomization depends on the implementation +but it can at least be assumed that the procedure sets s to a +different state for each subsequent run of the Scheme system. +
+
+ +
+
+(random-source-pseudo-randomize! s i j) +
+ +
+Changes the state of the random source s into the initial +state of the (i, j)-th independent random source, +where i and j are non-negative integers. +This procedure provides a mechanism to obtain a large number of +independent random sources (usually all derived from the same backbone +generator), indexed by two integers. +In contrast to random-source-randomize!, +this procedure is entirely deterministic. +
+
+ + +
+ +
+ +
+(random-source-make-integers s) -> rand +
+ +
+Obtains a procedure rand to generate random integers +using the random source s. +Rand takes a single argument n, +which must be a positive integer, and returns the next uniformly +distributed random integer from the interval {0, ..., n-1} +by advancing the state of the source s.

+ +If an application obtains and uses several generators for the same +random source s, a call to any of these generators advances +the state of s. Hence, the generators do not produce +the same sequence of random integers each but rather share a state. +This also holds for all other types of generators derived from +a fixed random sources. +Implementations that support concurrency make sure that +the state of a generator is properly advanced.

+

+ +
+ +
+ +
+(random-source-make-reals s) -> rand
+(random-source-make-reals s unit) -> rand
+
+ +
+Obtains a procedure rand to generate random real numbers +0 < x < 1 using the random source s. +The procedure rand is called without arguments.

+ +The optional parameter unit determines the type of numbers +being produced by rand and the quantization of the output. +Unit must be a number such that 0 < unit < 1. +The numbers created by rand are of the same numerical +type as unit and the potential output values are +spaced by at most unit. One can imagine rand +to create numbers as x*unit where x +is a random integer in {1, ..., floor(1/unit)-1}. +Note, however, that this need not be the way the values +are actually created and that the actual resolution of +rand can be much higher than unit. +In case unit is absent it defaults to a reasonably +small value (related to the width of the mantissa of an +efficient number format). +

+ +
+ + +

Design Rationale

+ +

Why not combine random-integer and +random-real?

+ +The two procedures are not combined into a single variable-arity +procedures to save a little time and space during execution. +Although some Scheme systems can deal with variable arity as +efficiently as with fixed arity this is not always the case +and time efficiency is very important here. + +

Why not some object-oriented interface?

+ +There are many alternatives to the interface as specified in this SRFI. +In particular, every framework for object-orientation can be used to +define a class for random sources and specify the interface for the +methods on random sources. +However, as the object-oriented frameworks differ considerably +in terms of syntax and functionality, this SRFI does not make +use of any particular framework. + +

Why is there not just a generator with a fixed range?

+ +A bare fixed-range generator is of very limited use. +Nearly every application has to add some functionality +to make use of the random numbers. +The most fundamental task in manipulating +random numbers is to change the range and quantization. +This is exactly what is provided by +random-integer and random-real. +In addition, is saves the user from the pitfall of changing +the range with a simple modulo-computation +which may substantially reduce the quality of the +numbers being produced.

+ +The design of the interface is based on three prototype applications: +

    +
  1. +Repeatedly choose from relatively small sets: +As the size of the set is likely to vary from call to call, +random-integer accepts a range argument n in every call. +The implementation should try to avoid boxing/unboxing of values +if the ranges fit into immediate integers. +
  2. +Generate a few large integers with a fixed number of bits: +As generating the random number itself is expensive, +passing the range argument in every call does not hurt performance. +Hence, the same interface as in the first application can be used. +
  3. +Generate real numbers: +Unlike the choose-from-set case, +the range and the quantization is constant over a +potentially very large number of calls. +In addition, there are usually just a few distinct instances of +quantization and number type, most likely corresponding to +underlying float and double +representations. +Therefore, +random-real does not accept any parameters but +the procedure random-source-make-reals creates +a properly configured random-real procedure. +
+ +

Why bother about floating point numbers at all?

+ +A proper floating point implementation of a random number generator +is potentially much more efficient that an integer implementation +because it can use more powerful arithmetics hardware. +If in addition the application needs floating point random numbers +it would be an intolerable waste to run an integer generator to +produce floating point random numbers. +A secondary reason is to save the user from the 'not as easy as +it seems' task of converting an integer generator into a real +generator. + +

Why are zero and one excluded from random-real?

+ +The procedure random-real does not return +x = 0 or x = 1 in order to allow +(log x) and +(log (- 1 x)) +without the danger of a numerical exception. + +

Implementation

+ +

Choice of generator

+ +The most important decision about the implementation is +the choice of the random number generator. +The basic principle here is: Let quality prevail! +In the end, a performance penalty of a better generator may be +a cheap price to pay for some avoided catastrophes. +It may be unexpected, but I have also seen many examples +where the better generator was also the faster. +Simple linear congruential generator cannot be recommended +as they tend to be ill-behaved in several ways.

+ +For this reason, my initial proposal was George Marsaglia's +COMBO generator, which is the combination of a 32-bit +multiplicative lagged Fibonacci-generator with a 16-bit +multiply with carry generator. +The COMBO generator passes all tests of Marsaglia's +DIEHARD +testsuite for random number generators and has +a period of order 2^60.

+ +As an improvement, Brad Lucier suggested +suggested +Pierre L'Ecuyer's +MRG32k3a +generator which is combination of two recursive generators +of degree three, both of which fit into 54-bit arithmetics. +The MRG32k3a generator also passes DIEHARD and in addition, +has desireable spectral properties and a period in the +order of 2^191. +As a matter of fact, multiple recursive generators (MRGs) are +theoretically much better understood than special constructions +as the COMBO generator. +This is the reason why the implementations provided here +implements the MRG32k3a generator. +When implemented in floating point arithmetics with sufficient +mantissa-width, this generator is also very fast.

+ +

Choice of arithmetics

+ +The next important decision about the implementation is +the type of arithmetics to be used. +The choice is difficult and depends heavily on the +underlying Scheme system and even on the underlying +hardware platform and architecture. +For the MRG32k3a generator, use 64-bit arithmetics if you +really have it. If not, use a floating point ALU if +it gives you 54 or more bits of mantissa. +And if you do not have floats either, then at least +try to make sure you work with immediate integers +(instead of allocated objects). +Unfortunately, there is no portable way in Scheme to +find out about native and emulated arithmetics.

+ +As performance is critical to many applications, +one might want to implement the actual +generator itself in native code. +For this reason, I provide three different +implementations of the backbone generator +as a source of inspiration. +See the code below. + +

Data Type for Random Sources

+ +An important aspect of the specification in this SRFI +is that random sources are objects of a distinct type. +Although this is straight-forward and available in nearly +every Scheme implementation, there is no portable way +to do this at present. +One way to define the record type is to use +SRFI-9.

+ +The reference implementations below define a record +type to contain the exported procedures. +The actual state of the generator is stored in the +binding time environment of make-random-source. +This has the advantage that access to the state is fast +even if the record type would be slow (which need not be +the case). + +

Entropy Source for Randomization

+ +Another problematic part of the specification with respect +to portability is random-source-randomize! as +it needs access to a real entropy source.

+ +A reasonable choice for such as source is to use the system +clock in order to obtain a value for randomization, for example +in the way John David Stone recommends (see reference below). +This is good enough for most applications with the +notable exception of security related programs. +One way to obtain the time in Scheme is to use +SRFI-19.

+ +

Implementation of the specified interface

+ +Once the portability issues are resolved, +one can provide the remaining functionality as +specified in this SRFI document.

+ +For the reference implementation, a relatively large part +of the code deals with the more advanced features of the +MRG32k3a generator, +in particular random-source-pseudo-randomize!. +This code is inspired by Pierre L'Ecuyer's own implementation +of the MRG32k3a generator.

+ +Another part of this generic code deals with changing +the range and quantization of the random numbers and +with error checking to detect common mistakes and abuses.

+ +

Implementation Examples

+ +Here are three alternative implementations of the SRFI. +(Here are all files, tar-gzipped, 13300 bytes.) +Keep in mind that a SRFI is a "request for implementation", +which means these implementations are merely examples +to illustrate the specification and inspire people to implement +it better and faster. +The performance figures below are rough indications measured +on a Pentium3, 800 Mhz, Linux; x int/s, y real/s +means (random-integer 2) can be computed about x +times a second and (random-real) about y times a second. +The implementations are + +
    + +
  1. for Scheme 48 0.57, using 54-bit integer only. +This implementation aims at portability, not at performance +(30000 ints/s, 3000/s reals/s). + +
  2. for Scheme 48 0.57 with the core generator being implemented +in C using (double)-arithmetics. +The generator is made available in Scheme 48 via the +C/Scheme +interface. +The performance of this generator is good +(160000 ints/s, 180000 reals/s). + +
  3. for Gambit 3.0, using flonum and +54-bit integer. +This code is inspired by a program by Brad Lucier as +posted +to the discussion archive of this SRFI. +The performance of this generator is good when compiled +(5000 ints/s, 25000/s reals/s when interpreted, +200000 ints/s, 400000/s reals/s when compiled; +see acknowledgements). + +
+ +In addition to the implementations there is a small +collection of confidence tests +for the interface specified. +The tests merely check a few assertions expressed by the specification. +It is not the intention to provide a complete test of the interface here. +It is even less the intention to provide statistical tests of the +generator itself. +However, there is a function to write random bits from +the generators to a file in a way readable by the DIEHARD +testsuite. This makes it easier for implementors to find out +about their favorite generators and check their implementation.

+ + + +

Recommended Usage Patterns

+ +Unless the functionality defined in this SRFI is sufficient, +an application has to implement more procedures to construct +other random deviates. +This section contains some recommendation +on how to do this technically by presenting +examples of increasing difficulty +with respect to the interface. +Note that the code below is not part of the specification, +it is merely meant to illustrate the spirit + +

Generating Random Permutations

+ +The following code defines procedures to generate random +permutations of the set {0, ..., n-1}. +Such a permutation is represented by a vector +of length n for the images of the points.

+ +Observe that the implementation first defines the procedure +random-source-make-permutations to +turn a random source s into a procedure to generate +permutations of given degree n. +In a second step, this is applied to the default source +to define a ready-to-use procedure for permutations: +(random-permutation n) +constructs a random permutation of degree n. + +

+(define (random-source-make-permutations s)
+  (let ((rand (random-source-make-integers s)))
+    (lambda (n)
+      (let ((x (make-vector n 0)))
+	(do ((i 0 (+ i 1)))
+	    ((= i n))
+	  (vector-set! x i i))
+	(do ((k n (- k 1)))
+	    ((= k 1) x)
+	  (let* ((i (- k 1))
+		 (j (rand k))
+		 (xi (vector-ref x i))
+		 (xj (vector-ref x j)))
+	    (vector-set! x i xj)
+	    (vector-set! x j xi)))))))
+
+(define random-permutation
+  (random-source-make-permutations default-random-source))
+
+ +For the algorithm refer to Knuth's "The Art of Computer Programming", +Vol. II, 2nd ed., Algorithm P of Section 3.4.2. + +

Generating Exponentially-Distributed Random Numbers

+ +The following code defines procedures to generate exponentially +Exp(mu)-distributed random numbers. +The technical difficulty of the interface addressed here is +how to pass optional arguments to random-source-make-reals. + +
+(define (random-source-make-exponentials s . unit)
+  (let ((rand (apply random-source-make-reals s unit)))
+    (lambda (mu)
+      (- (* mu (log (rand)))))))
+
+(define random-exponential
+  (random-source-make-exponentials default-random-source))
+
+ +The algorithm is folklore. Refer to Knuth's "The Art of Computer +Programming", Vol. II, 2nd ed., Section 3.4.1.D. + +

Generating Normally-Distributed Random Numbers

+ +The following code defines procedures to generate +normal N(mu, sigma)-distributed real numbers using +the polar method.

+ +The technical difficulty of the interface addressed here +is that the polar method generates two results per computation. +We return one of the result and store the second one to be +returned by the next call to the procedure. +Note that this implies that random-source-state-set! +(and the other procedures modifying the state) does not necessarily +affect the output of random-normal immediately! + +

+(define (random-source-make-normals s . unit)
+  (let ((rand (apply random-source-make-reals s unit))
+	(next #f))
+    (lambda (mu sigma)
+      (if next
+	  (let ((result next))
+	    (set! next #f)
+	    (+ mu (* sigma result)))
+	  (let loop ()
+	    (let* ((v1 (- (* 2 (rand)) 1))
+		   (v2 (- (* 2 (rand)) 1))
+		   (s (+ (* v1 v1) (* v2 v2))))
+	      (if (>= s 1)
+		  (loop)
+		  (let ((scale (sqrt (/ (* -2 (log s)) s))))
+		    (set! next (* scale v2))
+		    (+ mu (* sigma scale v1))))))))))
+
+(define random-normal
+  (random-source-make-normals default-random-source))
+
+ +For the algorithm refer to Knuth's "The Art of Computer Programming", +Vol. II, 2nd ed., Algorithm P of Section 3.4.1.C. + +

Acknowledgements

+ +I would like to thank all people who have participated in the discussion, +in particular Brad Lucier and Pierre l'Ecuyer. +Their contributions have greatly improved the design of this SRFI. +Moreover, Brad has optimized the Gambit implementation quite substantially. + +

References

+ +
    + +
  1. + G. Marsaglia: + Diehard -- Testsuite for Random Number Generators. + stat.fsu.edu/~geo/diehard.html + (Also contains some generators that do pass Diehard.) +
  2. + +
  3. + D. E. Knuth: + The Art of Computer Programming; + Volume II Seminumerical Algorithms. + 2nd ed. Addison-Wesley, 1981. + (The famous chapter on random number generators.) +
  4. + +
  5. + P. L'Ecuyer: + "Software for Uniform Random Number Generation: + Distinguishing the Good and the Bad", + Proceedings of the 2001 Winter Simulation Conference, + IEEE Press, Dec. 2001, 95--105. + www.iro.umontreal.ca/~lecuyer/myftp/papers/wsc01rng.pdf + (Profound discussion of random number generators.) +
  6. + +
  7. + P. L'Ecuyer: + "Good Parameter Sets for Combined Multiple Recursive + Random Number Generators", + Shorter version in Operations Research, 47, 1 (1999), 159--164. + www.iro.umontreal.ca/~lecuyer/myftp/papers/combmrg2.ps + (Actual numbers for good generators.) +
  8. + +
  9. + P. L'Ecuyer: + "Software for Uniform Random Number Generation: + Distinguishing the Good and the Bad", + Proceedings of the 2001 Winter Simulation Conference, + IEEE Press, Dec. 2001, 95--105. +
  10. + +
  11. + MIT Scheme v7.6: + random flo:random-unit *random-state* make-random-state + random-state? + http://www.swiss.ai.mit.edu/projects/scheme/documentation/scheme_5.html#SEC53 + (A mechanism to run a fixed unspecified generator.) +
  12. + +
  13. + A. Jaffer: + SLIB 2d2 with (require 'random): + random *random-state* copy-random-state seed->random-state + make-random-state random:uniform random:exp random:normal-vector! + random-hollow-sphere! random:solid-sphere! + http://www.swiss.ai.mit.edu/~jaffer/slib_4.html#SEC92 + (Based on the MIT Scheme mechanism.) +
  14. + +
  15. + R. Kelsey, J. Rees: + Scheme 48 v0.57 'random.scm': + make-random make-random-vector + (Internal procedures of Scheme48; a fixed 28-bit generator.) +
  16. + +
  17. + M. Flatt: + PLT MzScheme Version 200alpha1: + random random-seed current-pseudo-random-generator + make-pseudo-random-generator pseudo-random-generator? + http://download.plt-scheme.org/doc/200alpha1/html/mzscheme/mzscheme-Z-H-3.html#%_idx_144 + (A mechanism to run a generator and to exchange the generator.) +
  18. + +
  19. + H. Abelson, G. J. Sussmann, J. Sussman: + Structure and Interpretation of Computer Programs. + http://mitpress.mit.edu/sicp/full-text/book/book-Z-H-20.html#%_idx_2934 + (The rand-example shows a textbook way to define a + random number generator.) +
  20. + +
  21. + John David Stone: + A portable random-number generator. + http://www.math.grin.edu/~stone/events/scheme-workshop/random.html + (An implementation of a linear congruental generator in Scheme.) +
  22. + +
  23. + Network Working Group: + RFC1750: Randomness Recommendations for Security. + http://www.cis.ohio-state.edu/htbin/rfc/rfc1750.html + (A serious discussion of serious randomness for serious security.) +
  24. + +
  25. + http://www.random.org/essay.html
    + http://www.taygeta.com/random/randrefs.html + (Resources on random number generators and randomness.) +
  26. + +
+ + +

Copyright

+

Copyright (C) Sebastian Egner (2002). All Rights Reserved.

+ +

+Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: +

+

+The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. +

+

+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +

+ +
+
Editor: Mike Sperber
+
Author: Sebastian Egner
+ + +Last modified: Fri Sep 5 16:11:17 MST 2003 + + + diff --git a/doc/srfi-std/srfi-28.html b/doc/srfi-std/srfi-28.html new file mode 100644 index 0000000000..8aae23b3ce --- /dev/null +++ b/doc/srfi-std/srfi-28.html @@ -0,0 +1,181 @@ + + + + + SRFI 28: Basic Format Strings + + + + + + +

Title

+ + SRFI 28: Basic Format Strings + +

Author

+ + Scott G. Miller + +

Status

+ + This SRFI is currently in ``final'' status. To see an explanation of each status that a SRFI can hold, see here. + + You can access the discussion on this SRFI via the archive of the mailing list. + + + +

Abstract

+ + This document specifies Format Strings, a method of + interpreting a Scheme string which contains a number of escape + sequences that are replaced with other string data according to + the semantics of each sequence. + +

Rationale

+ + Many Scheme systems provide access to a function called + format. This function takes as arguments a format string, + an ordinary Scheme string containing zero or more escape + sequences, followed zero or more Scheme values. The procedure + processes the format string and performs string replacement on + the escape sequences according to the rules for each code. This + SRFI defines a basic version of format which should + allow portable code to be written using the function without + much (if any) effort on the part of Scheme implementors. + +

Specification

+ + format format-string [obj ...] -> + string + +
+

Accepts a message template (a Scheme String), and + processes it, replacing any escape sequences in order with + one or more characters, the characters themselves dependent + on the semantics of the escape sequence encountered.

+ +

An escape sequence is a two character sequence in the + string where the first character is a tilde '~'. Each escape + code's meaning is as follows:

+ + + +

~a and ~s, when encountered, require a + corresponding Scheme value to be present after the format + string. The values provided as operands are used by the + escape sequences in order. It is an error if fewer values are + provided than escape sequences that require them.

+ +

~% and ~~ require no corresponding + value.

+
+ +

Examples

+
+(format "Hello, ~a" "World!")
+; => "Hello, World!"
+
+(format "Error, list is too short: ~s~%" '(one "two" 3))
+; => "Error, list is too short: (one \"two\" 3))"
+
+ +

Implementation

+ + The implementation below requires SRFI-6 (Basic string ports) + and SRFI-23 (Error reporting mechanism).
+ +
+(define format
+  (lambda (format-string . objects)
+    (let ((buffer (open-output-string)))
+      (let loop ((format-list (string->list format-string))
+                 (objects objects))
+        (cond ((null? format-list) (get-output-string buffer))
+              ((char=? (car format-list) #\~)
+               (if (null? (cdr format-list))
+                   (error 'format "Incomplete escape sequence")
+                   (case (cadr format-list)
+                     ((#\a)
+                      (if (null? objects)
+                          (error 'format "No value for escape sequence")
+                          (begin
+                            (display (car objects) buffer)
+                            (loop (cddr format-list) (cdr objects)))))
+	             ((#\s)
+                      (if (null? objects)
+                          (error 'format "No value for escape sequence")
+                          (begin
+                            (write (car objects) buffer)
+                            (loop (cddr format-list) (cdr objects)))))
+                     ((#\%)
+                      (newline buffer)
+                      (loop (cddr format-list) objects))
+                     ((#\~)
+                      (write-char #\~ buffer)
+                      (loop (cddr format-list) objects))
+                     (else
+                      (error 'format "Unrecognized escape sequence")))))
+              (else (write-char (car format-list) buffer)
+                    (loop (cdr format-list) objects)))))))
+    
+
+ +

Copyright

+ +

Copyright (C) Scott G. Miller (2002). All Rights Reserved.

+ +

+ Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, copy, + modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: +

+

+ The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. +

+

+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +

+
+ +
+ Editor: + Francisco Solsona +
+ +
+ Author: Scott G. + Miller +
+ + + Last modified: Mon Apr 8 06:54:37 CDT 2002 + + + + diff --git a/doc/srfi-std/srfi-29.html b/doc/srfi-std/srfi-29.html new file mode 100644 index 0000000000..93604a4eef --- /dev/null +++ b/doc/srfi-std/srfi-29.html @@ -0,0 +1,507 @@ + + + + + SRFI 29: Localization + + + + +

Title

+ + SRFI 29: Localization + +

Author

+ + Scott G. Miller + +

Abstract

+ + This document specifies an interface to retrieving and + displaying locale sensitive messages. A Scheme program can + register one or more translations of templated messages, and + then write Scheme code that can transparently retrieve the + appropriate message for the locale under which the Scheme + system is running.
+ + +

Rationale

+ +

As any programmer that has ever had to deal with making his + or her code readable in more than one locale, the process of + sufficiently abstracting program messages from their + presentation to the user is non-trivial without help from the + programming language. Most modern programming language + libraries do provide some mechanism for performing this + separation.

+ +

A portable API that allows a piece of code to run without + modification in different countries and under different + languages is a must for any non-trivial software project. +  The interface should separate the logic of a program from + the myriad of translations that may be necessary.

+ +

The interface described in this document provides such + functionality. The underlying implementation is also allowed to + use whatever datastructures it likes to provide access to the + translations in the most efficient manner possible.  In + addition, the implementation is provided with standardized + functions that programs will use for accessing an external, + unspecified repository of translations.

+ +

This interface does not cover all aspects of + localization, including support for non-latin characters, + number and date formatting, etc. Such functionality is the + scope of a future SRFI that may extend this one.

+ +

Dependencies

+ + An SRFI-29 conformant implementation must also implement + SRFI-28, Basic Format Strings. Message templates are strings + that must be processed by the format function + specified in that SRFI. + +

Specification

+ +

Message Bundles

+ +

A Message Bundle is a set of message templates and their + identifying keys. Each bundle contains one or more such + key/value pairs. The bundle itself is associated with a + bundle specifier which uniquely identifies the + bundle.

+ +

Bundle Specifiers

+ +

A Bundle Specifier is a Scheme list that describes, in order + of importance, the package and locale of a message bundle. +  In most cases, a locale specifier will have between one + and three elements. The first element is a symbol denoting the + package for which this bundle applies. The second and third + elements denote a locale. The second element (first + element of the locale) if present, is the two letter, ISO 639-1 + language code for the bundle. The third element, if present, is + a two letter ISO 3166-1 country code.  In some cases, a + fourth element may be present, specifying the encoding used for + the bundle.  All bundle specifier elements are Scheme + symbols.

+ +

If only one translation is provided, it should be designated + only by a package name, for example (mathlib). This + translation is called the default translation.

+ +

Bundle Searching

+ +

When a message template is retrieved from a bundle, the + Scheme implementation will provide the locale under which the + system is currently running. When the template is retrieved, + the package name will be specified. The Scheme system should + construct a Bundle Specifier from the provided package name and + the active locale. For example, when retrieving a message + template for French Canadian, in the mathlib package, + the bundle specifier '(mathlib fr ca)' is used. A + program may also retrieve the elements of the current locale + using the no-argument procedures:

+ +

current-language -> + symbol
+ current-language symbol -> + undefined
+

+ +
+ When given no arguments, returns the current ISO 639-1 + language code as a symbol.  If provided with an + argument, the current language is set to that named by the + symbol for the currently executing Scheme thread (or for the + entire Scheme system if such a distinction is not possible). +   +
+ +

current-country -> + symbol
+ current-country symbol -> + undefined
+

+ +
+ returns the current ISO 3166-1 country code as a symbol. +  If provided with an argument, the current country is + set to that named by the symbol for the currently executing + Scheme thread (or for the entire Scheme system if such a + distinction is not possible).    +
+ +

current-locale-details -> list of + symbols
+ current-locale-details list-of-symbols -> + undefined
+

+ +
+ Returns a list of additional locale details as a list of + symbols.  This list may contain information about + encodings or other more specific information.  If + provided with an argument, the current locale details are set + to those given in the currently executing Scheme thread (or + for the entire Scheme system if such a distinction is not + possible).  +
+ +

The Scheme System should first check for a bundle with the + exact name provided. If no such bundle is found, the last + element from the list is removed and a search is tried for a + bundle with that name. If no bundle is then found, the list is + shortened by removing the last element again. If no message is + found and the bundle specifier is now the empty list, an error + should be raised.

+ +

The reason for this search order is to provide the most + locale sensitive template possible, but to fall back on more + general templates if a translation has not yet been provided + for the given locale.

+ +

Message Templates

+ +

A message template is a localized message that may or may + not contain one of a number of formatting codes. A message + template is a Scheme string. The string is of a form that can + be processed by the format procedure found in many + Scheme systems and formally specified in SRFI-28 (Basic Format + Strings).

+ +

This SRFI also extends SRFI-28 to provide an additional + format escape code:

+ +
+ ~[n]@* - Causes a value-requiring escape code that + follows this code immediately to reference the [N]'th + optional value absolutely, rather than the next unconsumed + value. The referenced value is not consumed. +
+ This extension allows optional values to be positionally + referenced, so that message templates can be constructed that + can produce the proper word ordering for a language. + +

Preparing Bundles

+ Before a bundle may be used by the Scheme system to retrieve + localized template messages, they must be made available to the + Scheme system.  This SRFI specifies a way to portably + define the bundles, as well as store them in and retrieve them + from an unspecified system which may be provided by resources + outside the Scheme system.
+ + +

declare-bundle! bundle-specifier + association-list -> undefined
+

+ +
+ Declares a new bundle named by the given bundle-specifier. +  The contents of the bundle are defined by the provided + association list.  The list contains associations + between Scheme symbols and the message templates (Scheme + strings) they name.  If a bundle already exists with the + given name, it is overwritten with the newly declared + bundle.
+
+ store-bundle bundle-specifier -> + boolean
+ + +
+ Attempts to store a bundle named by the given bundle + specifier, and previously made available using + declare-bundle! or load-bundle!, in an + unspecified mechanism that may be persistent across Scheme + system restarts.  If successful, a non-false value is + returned.  If unsuccessful, #f is returned.
+
+ load-bundle! bundle-specifier -> + boolean
+ + +
+ Attempts to retrieve a bundle from an unspecified mechanism + which stores bundles outside the Scheme system.  If the + bundle was retrieved successfully, the function returns a + non-false value, and the bundle is immediately available to + the Scheme system. If the bundle could not be found or loaded + successfully, the function returns #f, and the + Scheme system's bundle registry remains unaffected.
+
+ A compliant Scheme system may choose not to provide any + external mechanism to store localized bundles.  If it does + not, it must still provide implementations for + store-bundle and load-bundle!. In such a + case, both functions must return #f regardless of the + arguments given. Users of this SRFI should recognize that the + inability to load or store a localized bundle in an external + repository is not a fatal error.
+ + +

Retrieving Localized Message Templates

+ +

localized-template package-name + message-template-name -> string or #f
+

+ +
+ Retrieves a localized message template for the given package + name and the given message template name (both symbols). +  If no such message could be found, false (#f) is + returned.
+
+
+ After retrieving a template, the calling program can use + format to produce a string that can be displayed to + the user.
+ + +

Examples

+ The below example makes use of SRFI-29 to display simple, + localized messages.  It also defines its bundles in such a + way that the Scheme system may store and retrieve the bundles + from a more efficient system catalog, if available.
+ +
+(let ((translations
+       '(((en) . ((time . "Its ~a, ~a.")
+                (goodbye . "Goodbye, ~a.")))
+         ((fr) . ((time . "~1@*~a, c'est ~a.")
+                (goodbye . "Au revoir, ~a."))))))
+  (for-each (lambda (translation)
+              (let ((bundle-name (cons 'hello-program (car translation))))
+                (if (not (load-bundle! bundle-name))
+                    (begin
+                     (declare-bundle! bundle-name (cdr translation))
+                     (store-bundle! bundle-name)))))
+             translations))
+
+(define localized-message
+  (lambda (message-name . args)
+    (apply format (cons (localized-template 'hello-program
+                                            message-name)
+                        args))))
+
+(let ((myname "Fred"))
+  (display (localized-message 'time "12:00" myname))
+  (display #\newline)
+
+  (display (localized-message 'goodbye myname))
+  (display #\newline))
+
+;; Displays (English):
+;; Its 12:00, Fred.
+;; Goodbye, Fred.
+;;
+;; French:
+;; Fred, c'est 12:00.
+;; Au revoir, Fred.
+
+ +

Implementation

+ +

The implementation requires that the Scheme system provide a + definition for current-language and + current-country capable of distinguishing the correct + locale present during a Scheme session. The definitions of + those functions in the reference implementation are not capable + of that distinction. Their implementation is provided only so + that the following code can run in any R4RS scheme system. +  
+

+ +

In addition, the below implementation of a compliant + format requires SRFI-6 (Basic String Ports) and + SRFI-23 (Error reporting)

+
+;; The association list in which bundles will be stored
+(define *localization-bundles* '())
+
+;; The current-language and current-country functions provided
+;; here must be rewritten for each Scheme system to default to the
+;; actual locale of the session
+(define current-language
+  (let ((current-language-value 'en))
+    (lambda args
+      (if (null? args)
+          current-language-value
+          (set! current-language-value (car args))))))
+
+(define current-country
+  (let ((current-country-value 'us))
+    (lambda args
+      (if (null? args)
+          current-country-value
+          (set! current-country-value (car args))))))
+
+;; The load-bundle! and store-bundle! both return #f in this
+;; reference implementation.  A compliant implementation need
+;; not rewrite these procedures.
+(define load-bundle!
+  (lambda (bundle-specifier)
+    #f))
+
+(define store-bundle!
+  (lambda (bundle-specifier)
+    #f))
+
+;; Declare a bundle of templates with a given bundle specifier
+(define declare-bundle!
+  (letrec ((remove-old-bundle
+            (lambda (specifier bundle)
+              (cond ((null? bundle) '())
+                    ((equal? (caar bundle) specifier)
+                     (cdr bundle))
+                    (else (cons (car bundle)
+                                (remove-old-bundle specifier
+                                                   (cdr bundle))))))))
+    (lambda (bundle-specifier bundle-assoc-list)
+      (set! *localization-bundles*
+            (cons (cons bundle-specifier bundle-assoc-list)
+                  (remove-old-bundle bundle-specifier
+                                     *localization-bundles*))))))
+
+;;Retrieve a localized template given its package name and a template name
+(define localized-template
+  (letrec ((rdc
+            (lambda (ls)
+              (if (null? (cdr ls))
+                  '()
+                  (cons (car ls) (rdc (cdr ls))))))
+           (find-bundle
+            (lambda (specifier template-name)
+              (cond ((assoc specifier *localization-bundles*) =>
+                     (lambda (bundle) bundle))
+                    ((null? specifier) #f)
+                    (else (find-bundle (rdc specifier)
+                                       template-name))))))
+    (lambda (package-name template-name)
+      (let loop ((specifier (cons package-name
+                                  (list (current-language)
+                                        (current-country)))))
+        (and (not (null? specifier))
+             (let ((bundle (find-bundle specifier template-name)))
+               (and bundle
+                    (cond ((assq template-name bundle) => cdr)
+                          ((null? (cdr specifier)) #f)
+                          (else (loop (rdc specifier)))))))))))
+
+;;An SRFI-28 and SRFI-29 compliant version of format.  It requires
+;;SRFI-23 for error reporting.
+(define format
+  (lambda (format-string . objects)
+    (let ((buffer (open-output-string)))
+      (let loop ((format-list (string->list format-string))
+                 (objects objects)
+                 (object-override #f))
+        (cond ((null? format-list) (get-output-string buffer))
+              ((char=? (car format-list) #\~)
+               (cond ((null? (cdr format-list))
+                      (error 'format "Incomplete escape sequence"))
+                     ((char-numeric? (cadr format-list))
+                      (let posloop ((fl (cddr format-list))
+                                    (pos (string->number
+                                          (string (cadr format-list)))))
+                        (cond ((null? fl)
+                               (error 'format "Incomplete escape sequence"))
+                              ((and (eq? (car fl) '#\@)
+                                    (null? (cdr fl)))
+                                    (error 'format "Incomplete escape sequence"))
+                              ((and (eq? (car fl) '#\@)
+                                    (eq? (cadr fl) '#\*))
+                               (loop (cddr fl) objects (list-ref objects pos)))
+                              (else
+                                (posloop (cdr fl)
+                                         (+ (* 10 pos)
+                                            (string->number
+                                             (string (car fl)))))))))
+                     (else
+                       (case (cadr format-list)
+                         ((#\a)
+                          (cond (object-override
+                                 (begin
+                                   (display object-override buffer)
+                                   (loop (cddr format-list) objects #f)))
+                                ((null? objects)
+                                 (error 'format "No value for escape sequence"))
+                                (else
+                                  (begin
+                                    (display (car objects) buffer)
+                                    (loop (cddr format-list)
+                                          (cdr objects) #f)))))
+                         ((#\s)
+                          (cond (object-override
+                                 (begin
+                                   (display object-override buffer)
+                                   (loop (cddr format-list) objects #f)))
+                                ((null? objects)
+                                 (error 'format "No value for escape sequence"))
+                                (else
+                                  (begin
+                                    (write (car objects) buffer)
+                                    (loop (cddr format-list)
+                                          (cdr objects) #f)))))
+                         ((#\%)
+                          (if object-override
+                              (error 'format "Escape sequence following positional override does not require a value"))
+                          (display #\newline buffer)
+                          (loop (cddr format-list) objects #f))
+                        ((#\~)
+                          (if object-override
+                              (error 'format "Escape sequence following positional override does not require a value"))
+                          (display #\~ buffer)
+                          (loop (cddr format-list) objects #f))
+                         (else
+                           (error 'format "Unrecognized escape sequence"))))))
+              (else (display (car format-list) buffer)
+                    (loop (cdr format-list) objects #f)))))))
+
+
+ +

Copyright

+ + Copyright (C) Scott G. Miller (2002). All Rights Reserved. + +

This document and translations of it may be copied and + furnished to others, and derivative works that comment on or + otherwise explain it or assist in its implementation may be + prepared, copied, published and distributed, in whole or in + part, without restriction of any kind, provided that the above + copyright notice and this paragraph are included on all such + copies and derivative works. However, this document itself may + not be modified in any way, such as by removing the copyright + notice or references to the Scheme Request For Implementation + process or editors, except as needed for the purpose of + developing SRFIs in which case the procedures for copyrights + defined in the SRFI process must be followed, or as required to + translate it into languages other than English.

+ +

The limited permissions granted above are perpetual and will + not be revoked by the authors or their successors or + assigns.

+ +

This document and the information contained herein is + provided on an "AS IS" basis and THE AUTHOR AND THE SRFI + EDITORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION + HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES + OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.

+
+ +
+ Editor: David + Rush +
+ +
+ Author: Scott G. + Miller +
+ + Last modified: Mon Jun 17 12:00:08 Pacific + Daylight Time 2002
+ + + diff --git a/doc/srfi-std/srfi-30.html b/doc/srfi-std/srfi-30.html new file mode 100644 index 0000000000..b6c2fdcd05 --- /dev/null +++ b/doc/srfi-std/srfi-30.html @@ -0,0 +1,210 @@ + + + + SRFI 30: Nested Multi-line Comments + + + + +

Title

+ +SRFI 30: Nested Multi-line Comments + +

Author

+ +Martin Gasbichler + +

Status

+ +This SRFI is currently in ``final'' status. To see an explanation of +each status that a SRFI can hold, see here. It will +remain in draft until 2002-04-06, or as amended. to provide input on +this SRFI, please + +mail to srfi-30@srfi.schemers.org. +See +instructions here to subscribe to the list. You can access +previous messages via + +the archive of the mailing list. +You can access +post-finalization messages via + +the archive of the mailing list. + + + +

Related SRFIs

+ +

SRFI 22 defines + a multi line comment that may only appear at the beginning of a + file.

+ +

SRFI 10 + proposes the notation + +

 "#" <discriminator> <other-char>*
for further + SRFIs that introduce values which may be read and written.

+ +

Abstract

+ +

This SRFI extends R5RS by possibly nested, multi-line + comments. Multi-line comments start with #| and end + with |#.

+ +

Rationale

+ +

Multi-line comments are common to many programming languages. They + provide a convenient mean for adding blocks of text inside a program and + support development by fading out incomplete code or alternative + implementations.

+ +

The syntax #| ... |# was chosen + because it is already widely used (Chez Scheme, Chicken, Gambit-C, + Kawa, MIT Scheme, MzScheme and RScheme) and since it fits smoothly + with R5RS. Nested comments are an important feature for + incrementally commenting out code.

+ +

Specification

+ +

This SRFI extends the specification of comments -- R5RS + section 2.2 -- as follows:

+ + +

The sequence #| indicates the start of a + multi-line comment. The multi-line comment continues until + |# appears. If the closing |# is + omitted, an error is signaled. Multi-line comments are visible to + Scheme as a single whitespace. Multi-line comments may be nested: + Every #| within the comment starts a new multi-line + comment, which has to be terminated by a matching + |#.

+ +

This SRFI furthermore extends the production for + <comment> in the specification of lexical + structure -- R5RS + section 7.1.1 -- to: +

+
+    <comment> ---> ; <all subsequent characters up to a line break>
+     	    |         #| <comment-text> (<comment> <comment-text>)* |#
+
+    <comment-text> ---> <character sequence not containing #| or |#>
+
+    
+ + +

<delimiter> and #

+ +

The SRFI does not extend the specification of + <delimiter> from R5RS + section 7.1.1. It is therefore required to separate tokens + which require implicit termination (identifiers, numbers, + characters, and dot) from multi-line comments by a + <delimiter> from R5RS.

+ +

Rationale An extension of the + <delimiter> to #| would be + incompatible with existing implementations which allow + # as legal character within identifiers.

+ +

Implementation

+ +

The following procedure skip-comment deletes a + leading multi-line comment from the current input port. Its + optional argument specifies whether the caller has already removed + leading characters of the comment from the current input port. The + symbol start means that the caller has removed + nothing, read-sharp means that it has removed a sharp + and finally read-bar indicates that the caller has + removed #|.

+ +
+(define (skip-comment! . maybe-start-state)
+  (let lp ((state (if (null? maybe-start-state) 'start (car maybe-start-state))) 
+	   (nested-level 0))
+    (define (next-char)
+      (let ((c (read-char)))
+	(if (eof-object? c)
+	    (error "EOF inside block comment -- #| missing a closing |#")
+	    c)))
+
+    (case state
+      ((start) (case (next-char)
+		 ((#\|) (lp 'read-bar nested-level))
+		 ((#\#) (lp 'read-sharp nested-level))
+		 (else (lp 'start nested-level))))
+      ((read-bar) (case (next-char)
+		    ((#\#) (if (> nested-level 1)
+			       (lp 'start (- nested-level 1))))
+		    (else (lp 'start nested-level))))
+      ((read-sharp) (case (next-char)
+		      ((#\|) (lp 'start (+ nested-level 1)))
+		      (else (lp 'start nested-level)))))))
+    
+ +

A SRFI 22 script to remove nested multi-line comments is + available at + + http://srfi.schemers.org/srfi-30/remove-srfi30-comments-script.scm. + + The script will read a Scheme program containing nested + multi-line comments from standard input and emit the same + programs with the comments removed to standard output. The + script mimics the Scheme 48 reader and uses the + error procedure from SRFI 23.

+ +

A number of Scheme implemenations already support this SRFI: + Chez Scheme, Chicken, Gambit-C, Kawa, MIT Scheme, MzScheme and + RScheme. Bigloo supports multi-line comments via + #!/!#. Among the major Scheme + implementations, Scheme 48 and Guile do not support this SRFI + yet.

+ +

Copyright

+

Copyright (C) Martin Gasbichler (2002). All Rights Reserved.

+ +

+ Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, copy, + modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: +

+

+ The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. +

+

+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +

+ + +
+
Editor: Mike Sperber
+ + +Last modified: Sun Sep 1 17:14:55 MST 2002 + + + diff --git a/doc/srfi-std/srfi-31.html b/doc/srfi-std/srfi-31.html new file mode 100644 index 0000000000..faf6eb936f --- /dev/null +++ b/doc/srfi-std/srfi-31.html @@ -0,0 +1,361 @@ + + + + + SRFI 31: A special form `rec' for recursive evaluation + + + + +

Title

+ +SRFI 31: A special form rec for recursive evaluation + +

Author

+ +Mirko Luedde <Mirko.Luedde@SAP.com> + +

Status

+ +This SRFI is currently in ``final'' status. To see an explanation of +each status that a SRFI can hold, see here. + +You can access the discussion via the +archive of the mailing list. + + + +

Abstract

+ +We propose the implementation of a special form +called rec. This form is a generalization and +combination of the forms rec and +named-lambda of [Clinger1985]. It allows the simple and +non-imperative construction of self-referential expressions. As an +important special case, it extends the A. Church form +lambda such that it allows the direct definition of +recursive procedures without using further special forms like +let or letrec, without using advanced +constructions like the H. B. Curry combinator and, unlike +define, without introducing variable bindings into the +external environment. + +

Rationale

+ +

General

Among the prominent features of the Scheme +programming language as defined in [KCR1998] +are the following. + +
    + +
  1. It has simple syntax.
  2. + +
  3. It encourages recursive definitions, e.g. by ensuring memory + efficient tail recursion.
  4. + +
  5. It supports non-imperative programming.
  6. + +
+ +Nevertheless Scheme does not provide a syntax for recursive +evaluations with the properties of + +
    + +
  1. being as simple, intuitive and close to the mathematical standard +notation as possible,
  2. + +
  3. allowing general recursion,
  4. + +
  5. being non-imperative.
  6. + +
+ +

Example

+ +

Problem 1

+ +Let us look at the factorial function. In mathematical notation this +function is expressed as + +
 
+  (F : N |--> 1,            if N = 0; 
+              N * F(N - 1), otherwise).
+
+ +This expression is a term and not a definition or proposition. + +

We investigate some approaches to express the factorial function in +Scheme. + +

+ +

Solution 1

+ +A solution to our problem was already provided in [Clinger1985] by the form +named-lambda. An even earlier solution with a slightly +different syntax was implemented in Kent Dybvig's Chez Scheme system. + +Using this special form, we can denote the factorial simply by + +
+(named-lambda (F N) 
+              (if (zero? N) 1 
+                (* N (F (- N 1)))))
+
+ +This expression is a function term that denotes the factorial in the +appropriate brevity. + +

However, the form named-lambda has been dropped from +later versions of the Scheme Report. Also it is missing in +state-of-the-art implementations such as Chez Scheme (6.0a) and MIT +Scheme (7.7.0). (The latter actually knows a form +named-lambda with different semantics). + +

Problem 2

+ +The constant stream of ones can be defined via + +
(define S (cons 1 (delay S)))
+ +As in the case of the factorial, we are able to define the recursive +object at the price of spending an externally bound name. Remedying +this with let or letrec leads to similar +objections as above. + +

Solution 2

+ +This particular case of the self-referential problem was solved by the +rec form in [Clinger1985]. + +This form allows writing + +
(rec S (cons 1 (delay S)))
+ +This expression is non-imperative and does not introduce an external +variable binding. + +

Also this form has been dropped from later versions of the Scheme +Report. Moreover, from our point of view this form alone is not +capable of solving Problem 1. The respective definition would look +like + +

+(rec F 
+     (lambda (N) 
+       (if (zero? N) 1 
+	 (* N (F (- N 1))))))
+
+ +This again does not seem quite as simple and intuitive as the +mathematical notation. + +

Proposal

+ +We therefore propose to implement the rec special form in +a generalized way that combines the advantages of the +named-lambda and rec forms. + +The factorial function could be written +
+(rec (F N) 
+     (if (zero? N) 1 
+       (* N (F (- N 1)))))
+
+ +

Specification

+ +

Syntax

+ +The following production rules are to be added to those of [KCR1998] (we reuse names of non-terminals). + +
    + +
  1. <derived expression> --> <rec expression>
  2. + +
  3. <rec expression> --> (rec <variable> +<expression>)
  4. + +
  5. <rec expression> --> (rec (<variable>+) +<body>)
  6. + +
+ +

Semantics

+ +Scheme versions such as [KCR1998] providing +define-syntax, syntax-rules, +letrec and lambda might implement +rec as follows. + +
+(define-syntax rec
+  (syntax-rules ()
+    ((rec (NAME . VARIABLES) . BODY)
+     (letrec ( (NAME (lambda VARIABLES . BODY)) ) NAME))
+    ((rec NAME EXPRESSION)
+     (letrec ( (NAME EXPRESSION) ) NAME))))
+
+ +

Test

+ +The following session shows in which way rec allows a +tail-recursive implementation of the factorial function. + +
+> (define F (rec (F N)
+		((rec (G K L)
+		   (if (zero? K) L
+		     (G (- K 1) (* K L)))) N 1)))
+> F
+#<procedure>
+> (F 0)
+1
+> (F 10)
+3628800
+
+ +

Acknowledgements

+ +The author thanks Al Petrofsky for the final solution and Hal Abelson, +Chris Hanson and others for their input. The work of the maintainers +of the SRFI forum is highly appreciated. + +

Bibliography

+ + + +

Copyright

+ +

Copyright (C) Dr. Mirko Luedde (2002). All Rights Reserved.

+ +

+Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: +

+

+The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. +

+

+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +

+ +
+ +
Author: Mirko Luedde
+ +
Editor: Francisco Solsona
+ + + + diff --git a/doc/srfi-std/srfi-34.html b/doc/srfi-std/srfi-34.html new file mode 100644 index 0000000000..aa4675711c --- /dev/null +++ b/doc/srfi-std/srfi-34.html @@ -0,0 +1,326 @@ + + + +SRFI 34: Exception Handling for Programs + + + +

Title

+ +SRFI 34: Exception Handling for Programs + +

Authors

+ +Richard Kelsey and Michael Sperber + +

Status

+ +This SRFI is currently in ``final'' status. To see an explanation of +each status that a SRFI can hold, see here. It will +remain in draft until 2002-10-20, or as amended. to provide input on +this SRFI, please mail to + +srfi-34@srfi.schemers.org. +See +instructions here to subscribe to the list. You can access +the discussion via + +the archive of the mailing list. +You can access +post-finalization messages via + +the archive of the mailing list. + + + +

Abstract

+This SRFI defines exception-handling and exception-raising constructs for Scheme, including +

This SRFI is based on (withdrawn) SRFI 12: Exception Handling +by William Clinger, R. Kent Dybvig, Matthew Flatt, and Marc Feeley.

+

Rationale

+

The goals of the exception mechanism specified in this SRFI are to help programmers share code which relies on exception handling, and to be easily added to existing Scheme systems.

+

This SRFI is primarily useful in conjunction with one or more companion SRFIs:

+ +

Specification

+

Exception handlers are one-argument procedures that determine the action the program takes when an exceptional situation is signalled. The system implicitly maintains a current exception handler.

+

The program raises an exception by invoking the current exception handler, passing to it an object encapsulating information about the exception. Any procedure accepting one argument may serve as an exception handler and any object may be used to represent an exception.

+

The system maintains the current exception handler as part of the dynamic environment of the program, akin to the current input or output port, or the context for dynamic-wind. The dynamic environment can be thought of as that part of a continuation that does not specify the destination of any returned values. It includes the current input and output ports, the dynamic-wind + context, and this SRFI's current exception handler. See the reference implementation for portable definitions of current-dynamic-environment + and with-dynamic-environment.

+

The initial current exception handler of the program is implementation-dependent. However, it should interrupt the program in some way visible to the user, either by aborting it, invoking a debugger, or some similar action.

+

Establishing Exception Handlers

+
(with-exception-handler handler + thunk)
+

Returns the result(s) of invoking thunk. Handler + must be a procedure that accepts one argument. It is installed as the current exception handler for the dynamic extent (as determined by dynamic-wind) of the invocation of thunk.

+
+
+
(guard ( +<var> <clause1 +> <clause2 +> ...) + <body>) (syntax)
+

Syntax: + Each <clause> should have the same form as a cond + clause

+

Semantics: + Evaluating a guard + form evaluates <body> with an exception handler that binds the raised object to <var> and within the scope of that binding evaluates the clauses as if they were the clauses of a cond + expression. That implicit cond + expression is evaluated with the continuation and dynamic environment of the guard + expression. If every <clause>'s <test> evaluates to false and there is no else + clause, then raise + is re-invoked on the raised object within the dynamic environment of the original call to raise + except that the current exception handler is that of the guard + expression.

+
+
+

Raising Exceptions

+
(raise obj)
+

Invokes the current exception handler on obj. The handler is called in the dynamic environment of the call to raise, except that the current exception handler is that in place for the call to with-exception-handler + that installed the handler being called. The handler's continuation is otherwise unspecified.

+
+
+

Examples

+
(call-with-current-continuation
+ (lambda (k)
+   (with-exception-handler (lambda (x)
+                             (display "condition: ")
+                             (write x)
+                             (newline)
+                             (k 'exception))
+     (lambda ()
+       (+ 1 (raise 'an-error))))))
+PRINTS: condition: an-error
+=> exception
+
+(call-with-current-continuation
+ (lambda (k)
+   (with-exception-handler (lambda (x)
+                             (display "something went wrong")
+                             (newline)
+                             'dont-care)
+     (lambda ()
+       (+ 1 (raise 'an-error))))))
+PRINTS: something went wrong
+then behaves in an unspecified way
+
+(guard (condition
+         (else
+          (display "condition: ")
+          (write condition)
+          (newline)
+          'exception))
+  (+ 1 (raise 'an-error)))
+PRINTS: condition: an-error
+=> exception
+
+(guard (condition
+         (else
+          (display "something went wrong")
+          (newline)
+          'dont-care))
+ (+ 1 (raise 'an-error)))
+PRINTS: something went wrong
+=> dont-care
+
+(call-with-current-continuation
+ (lambda (k)
+   (with-exception-handler (lambda (x)
+                             (display "reraised ") (write x) (newline)
+                             (k 'zero))
+     (lambda ()
+       (guard (condition
+                ((positive? condition) 'positive)
+                ((negative? condition) 'negative))
+        (raise 1))))))
+=> positive
+
+(call-with-current-continuation
+ (lambda (k)
+   (with-exception-handler (lambda (x)
+                             (display "reraised ") (write x) (newline)
+                             (k 'zero))
+     (lambda ()
+       (guard (condition
+                ((positive? condition) 'positive)
+                ((negative? condition) 'negative))
+        (raise -1))))))
+=> negative
+
+(call-with-current-continuation
+ (lambda (k)
+   (with-exception-handler (lambda (x)
+                             (display "reraised ") (write x) (newline)
+                             (k 'zero))
+     (lambda ()
+       (guard (condition
+                ((positive? condition) 'positive)
+                ((negative? condition) 'negative))
+        (raise 0))))))
+PRINTS: reraised 0
+=> zero
+
+(guard (condition
+         ((assq 'a condition) => cdr)
+         ((assq 'b condition)))
+  (raise (list (cons 'a 42))))
+=> 42
+
+(guard (condition
+         ((assq 'a condition) => cdr)
+         ((assq 'b condition)))
+  (raise (list (cons 'b 23))))
+=> (b . 23)
+

Reference Implementation

+

The reference implementation makes use of SRFI 9 + ("Defining Record Types"), and SRFI 23 + ("Error reporting mechanism").

+
(define *current-exception-handlers*
+  (list (lambda (condition)
+          (error "unhandled exception" condition))))
+
+(define (with-exception-handler handler thunk)
+  (with-exception-handlers (cons handler *current-exception-handlers*)
+                           thunk))
+
+(define (with-exception-handlers new-handlers thunk)
+  (let ((previous-handlers *current-exception-handlers*))
+    (dynamic-wind
+      (lambda ()
+        (set! *current-exception-handlers* new-handlers))
+      thunk
+      (lambda ()
+        (set! *current-exception-handlers* previous-handlers)))))
+
+(define (raise obj)
+  (let ((handlers *current-exception-handlers*))
+    (with-exception-handlers (cdr handlers)
+      (lambda ()
+        ((car handlers) obj)
+        (error "handler returned"
+               (car handlers)
+               obj)))))
+
+(define-syntax guard
+  (syntax-rules ()
+    ((guard (var clause ...) e1 e2 ...)
+     ((call-with-current-continuation
+       (lambda (guard-k)
+         (with-exception-handler
+          (lambda (condition)
+            ((call-with-current-continuation
+               (lambda (handler-k)
+                 (guard-k
+                  (lambda ()
+                    (let ((var condition))      ; clauses may SET! var
+                      (guard-aux (handler-k (lambda ()
+                                              (raise condition)))
+                                 clause ...))))))))
+          (lambda ()
+            (call-with-values
+             (lambda () e1 e2 ...)
+             (lambda args
+               (guard-k (lambda ()
+                          (apply values args)))))))))))))
+
+(define-syntax guard-aux
+  (syntax-rules (else =>)
+    ((guard-aux reraise (else result1 result2 ...))
+     (begin result1 result2 ...))
+    ((guard-aux reraise (test => result))
+     (let ((temp test))
+       (if temp 
+           (result temp)
+           reraise)))
+    ((guard-aux reraise (test => result) clause1 clause2 ...)
+     (let ((temp test))
+       (if temp
+           (result temp)
+           (guard-aux reraise clause1 clause2 ...))))
+    ((guard-aux reraise (test))
+     test)
+    ((guard-aux reraise (test) clause1 clause2 ...)
+     (let ((temp test))
+       (if temp
+           temp
+           (guard-aux reraise clause1 clause2 ...))))
+    ((guard-aux reraise (test result1 result2 ...))
+     (if test
+         (begin result1 result2 ...)
+         reraise))
+    ((guard-aux reraise (test result1 result2 ...) clause1 clause2 ...)
+     (if test
+         (begin result1 result2 ...)
+         (guard-aux reraise clause1 clause2 ...)))))
+

References

+ + +

Copyright

+ +

Copyright (C) Richard Kelsey, Michael Sperber (2002). All Rights +Reserved.

+ +

+Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: +

+

+The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. +

+

+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +

+ +
+
Editor: Francisco Solsona
+ diff --git a/doc/srfi-std/srfi-35.html b/doc/srfi-std/srfi-35.html new file mode 100644 index 0000000000..7e2964690e --- /dev/null +++ b/doc/srfi-std/srfi-35.html @@ -0,0 +1,515 @@ + +SRFI 35: Conditions +

Title

+ +Conditions + +

Authors

+ +Richard Kelsey and Michael Sperber + +

Status

+ +This SRFI is currently in ``final'' status. To see an explanation of +each status that a SRFI can hold, see here. It will +remain in draft until 2002-10-20, or as amended. to provide input on +this SRFI, please mail to + +srfi minus 35 at srfi dot schemers dot org. +See +instructions here to subscribe to the list. You can access +the discussion via + +the archive of the mailing list. +You can access +post-finalization messages via + +the archive of the mailing list. + + + +

Abstract

+

The SRFI defines constructs for creating and inspecting condition +types and values. A condition value encapsulates information about an +exceptional situation, or exception. This SRFI also defines a few basic +condition types.

+

Rationale

+

Conditions are values that communicate information about exceptional +situations between parts of a program. Code that detects an exception +may be in a different part of the program than the code that handles +it. In fact, the former may have been written independently from the +latter. Consequently, to facilitate effective handling of exceptions, +conditions must communicate as much information as possible as +accurately as possible, and still allow effective handling by code that +did not precisely anticipate the nature of the exception that occurred.

+

This SRFI provides two mechanisms to enable this kind of communication

+

+

Specification

+

Conditions are records with named fields. Each condition belongs to one or more condition types. +Each condition type specifies a set of field names. A condition +belonging to a condition type includes a value for each of the type's +field names. These values can be extracted from the condition by using +the appropriate field name.

+

There is a tree of condition types with the distinguished &condition + as its root. All other condition types have a parent condition type.

+

A condition belonging to several condition types with a common +supertype may have distinct values for the supertype's fields for each +type. The type used to access a field determines which of the values is +returned. The program can extract each of these field values separately.

+

Procedures

+
(make-condition-type id + parent + field-names)
+

Make-condition-type + returns a new condition type. Id + must be a symbol that serves as a symbolic name for the condition type. Parent + must itself be a condition type. Field-names + must be a list of symbols. It identifies the fields of the conditions associated with the condition type.

+

Field-names + must be disjoint from the field names of parent + and its ancestors.

+
+
(condition-type? thing)
+

Condition-type? + is a predicate for condition types: it returns #t + if thing + is a condition type, and #f + otherwise

+
+
(make-condition type + field-name + value + ...)
+

Make-condition + creates a condition value belonging condition type type. The following arguments must be, in turn, a field name and an arbitrary value. There must be such a pair for each field of type + and its direct and indirect supertypes. Make-condition + returns the condition value, with the argument values associated with their respective fields.

+
+
(condition? thing)
+

Condition? + is a predicate for conditions: it returns #t + if thing + is a condition type, and #f + otherwise

+
+
(condition-has-type? condition + condition-type)
+

Condition-has-type? + tests if condition condition + belongs to condition type condition-type. It returns #t + if any of condition +'s types includes condition-type +eitherdirectlyorasanancestorand + #f + otherwise.

+

It is an error if condition + is not a condition, or if condition-type + is not a condition type.

+
+
(condition-ref condition + field-name)
+

Condition + must be a condition, and field-name + a symbol. Moreover, condition + must belong to a condition type which has a field name called field-name, or one of its (direct or indirect) supertypes must have the field. Condition-ref + returns the value associated with field-name.

+

It is an error to refer to a field the condition does not have.

+
+
(make-compound-condition condition0 + + condition1 +...)
+

Make-compound-condition + returns a compound condition belonging to all condition types that the conditioni + + belong to.

+

Condition-ref, when applied to a compound condition will return the value from the first of the conditioni + + that has such a field.

+
+
(extract-condition condition + condition-type)
+

Condition + must be a condition belonging to condition-type. Extract-condition +returns a condition of condition type condition-type + with the field values specified by condition.

+

If condition + is a compound condition, extract-condition + extracts the field values from the subcondition belonging to condition-type + that appeared first in the call to make-compound-condition + that created the the condition. The returned condition may be newly created; it is possible for

(let* ((&c (make-condition-type 'c &condition '()))
+       (c0 (make-condition &c))
+       (c1 (make-compound-condition c0)))
+  (eq? c0 (extract-condition c1 &c)))
+
to return false.

+
+
+

Macros

+
(define-condition-type <condition-type> <supertype> <predicate> <field-spec> ...)
+

This defines a new condition type. <Condition-type>, <supertypes>, and <predicate> must all be identifiers. Define-condition-type +defines an identifier <condition-type> to some value describing +the condition type. <supertype> must be the name of a previously +defined condition type.

+

Define-condition-type also defines <predicate> to +a predicate that identifies conditions associated with that type, or +with any of its subtypes.

+

Each <field-spec> must be of the form ( +<field> <accessor>) + where both <field> and <accessor> must be identifiers. Define-condition-type +defines each <accessor> to a procedure which extracts the value +of the named field from a condition associated with this condition type.

+
+
(condition <type-field-binding> ...)
+

This creates a condition value. Each <type-field-binding> must be of the form ( +<condition-type> <field-binding> ...) +Each <field-binding> must be of the form ( +<field> <exp>) + where <field> is a field identifier from the definition of <condition-type>.

+

The <exp> are evaluated in some unspecified order; their +values can later be extracted from the condition object via the +accessors of the associated condition types or their supertypes.

+

The condition returned by condition + is created by a call of form

+
(make-compound-condition
+  (make-condition <condition-type> '<field-name> <value>...)
+  ...)
+

with the condition types retaining their order from thecondition +form. The field names and values are duplicated as necessary as described below.

+

Each <type-field-binding> must contain field bindings for all +fields of <condition-type> without duplicates. There is an +exception to this rule: if a field binding is missing, and the field +belongs to a supertype shared with one of the other +<type-field-binding> subforms, then the value defaults to that of +the first such binding in the condition + form.

+
+
+

Standard Conditions

+
&condition +
+

This is the root of the entire condition type hierarchy. It has a no fields.

+
+
&message +
+

This condition type could be defined by

(define-condition-type &message &condition
+  message-condition?
+  (message condition-message))
+

+

It carries a message further describing the nature of the condition to humans.

+
+
&serious +
+

This condition type could be defined by

(define-condition-type &serious &condition
+  serious-condition?)
+

+

This type describes conditions serious enough that they cannot +safely be ignored. This condition type is primarily intended as a +supertype of other condition types.

+
&error +
+

This condition type could be defined by

(define-condition-type &error &serious
+  error?)
+

+

This condition describes errors, typically caused by something that +has gone wrong in the interaction of the program with the external +world or the user.

+
+ +
+

Examples

+
(define-condition-type &c &condition
+  c?
+  (x c-x))
+
+(define-condition-type &c1 &c
+  c1?
+  (a c1-a))
+
+(define-condition-type &c2 &c
+  c2?
+  (b c2-b))
+(define v1 (make-condition &c1 'x "V1" 'a "a1"))
+
+(c? v1)        => #t
+(c1? v1)       => #t
+(c2? v1)       => #f
+(c-x v1)       => "V1"
+(c1-a v1)      => "a1"
+
+(define v2 (condition (&c2
+                        (x "V2")
+                        (b "b2"))))
+
+(c? v2)        => #t
+(c1? v2)       => #f
+(c2? v2)       => #t
+(c-x v2)       => "V2"
+(c2-b v2)      => "b2"
+
+(define v3 (condition (&c1
+                       (x "V3/1")
+                       (a "a3"))
+                      (&c2
+                       (b "b3"))))
+
+(c? v3)        => #t
+(c1? v3)       => #t
+(c2? v3)       => #t
+(c-x v3)       => "V3/1"
+(c1-a v3)      => "a3"
+(c2-b v3)      => "b3"
+
+(define v4 (make-compound-condition v1 v2))
+
+(c? v4)        => #t
+(c1? v4)       => #t
+(c2? v4)       => #t
+(c-x v4)       => "V1"
+(c1-a v4)      => "a1"
+(c2-b v4)      => "b2"
+
+(define v5 (make-compound-condition v2 v3))
+
+(c? v5)        => #t
+(c1? v5)       => #t
+(c2? v5)       => #t
+(c-x v5)       => "V2"
+(c1-a v5)      => "a3"
+(c2-b v5)      => "b2"
+

Reference Implementation

+

The reference implementation makes use of SRFI 1 + ("List Library"), SRFI 9 + ("Defining Record Types"), and SRFI 23 + ("Error reporting mechanism").

+
(define-record-type :condition-type
+  (really-make-condition-type name supertype fields all-fields)
+  condition-type?
+  (name condition-type-name)
+  (supertype condition-type-supertype)
+  (fields condition-type-fields)
+  (all-fields condition-type-all-fields))
+
+(define (make-condition-type name supertype fields)
+  (if (not (symbol? name))
+      (error "make-condition-type: name is not a symbol"
+             name))
+  (if (not (condition-type? supertype))
+      (error "make-condition-type: supertype is not a condition type"
+             supertype))
+  (if (not
+       (null? (lset-intersection eq?
+                                 (condition-type-all-fields supertype)
+                                 fields)))
+      (error "duplicate field name" ))
+  (really-make-condition-type name
+                              supertype
+                              fields
+                              (append (condition-type-all-fields supertype)
+                                      fields)))
+
+(define-syntax define-condition-type
+  (syntax-rules ()
+    ((define-condition-type ?name ?supertype ?predicate
+       (?field1 ?accessor1) ...)
+     (begin
+       (define ?name
+         (make-condition-type '?name
+                              ?supertype
+                              '(?field1 ...)))
+       (define (?predicate thing)
+         (and (condition? thing)
+              (condition-has-type? thing ?name)))
+       (define (?accessor1 condition)
+         (condition-ref (extract-condition condition ?name)
+                        '?field1))
+       ...))))
+
+(define (condition-subtype? subtype supertype)
+  (let recur ((subtype subtype))
+    (cond ((not subtype) #f)
+          ((eq? subtype supertype) #t)
+          (else
+           (recur (condition-type-supertype subtype))))))
+
+(define (condition-type-field-supertype condition-type field)
+  (let loop ((condition-type condition-type))
+    (cond ((not condition-type) #f)
+          ((memq field (condition-type-fields condition-type))
+           condition-type)
+          (else
+           (loop (condition-type-supertype condition-type))))))
+
+; The type-field-alist is of the form
+; ((<type> (<field-name> . <value>) ...) ...)
+(define-record-type :condition
+  (really-make-condition type-field-alist)
+  condition?
+  (type-field-alist condition-type-field-alist))
+
+(define (make-condition type . field-plist)
+  (let ((alist (let label ((plist field-plist))
+                 (if (null? plist)
+                            '()
+                     (cons (cons (car plist)
+                                 (cadr plist))
+                           (label (cddr plist)))))))
+    (if (not (lset= eq?
+                    (condition-type-all-fields type)
+                    (map car alist)))
+        (error "condition fields don't match condition type"))
+    (really-make-condition (list (cons type alist)))))
+
+(define (condition-has-type? condition type)
+  (any (lambda (has-type)
+         (condition-subtype? has-type type))
+       (condition-types condition)))
+
+(define (condition-ref condition field)
+  (type-field-alist-ref (condition-type-field-alist condition)
+                        field))
+
+(define (type-field-alist-ref type-field-alist field)
+  (let loop ((type-field-alist type-field-alist))
+    (cond ((null? type-field-alist)
+           (error "type-field-alist-ref: field not found"
+                  type-field-alist field))
+          ((assq field (cdr (car type-field-alist)))
+           => cdr)
+          (else
+           (loop (cdr type-field-alist))))))
+
+(define (make-compound-condition condition-1 . conditions)
+  (really-make-condition
+   (apply append (map condition-type-field-alist
+                      (cons condition-1 conditions)))))
+
+(define (extract-condition condition type)
+  (let ((entry (find (lambda (entry)
+                              (condition-subtype? (car entry) type))
+                            (condition-type-field-alist condition))))
+    (if (not entry)
+        (error "extract-condition: invalid condition type"
+                      condition type))
+    (really-make-condition
+      (list (cons type
+                  (map (lambda (field)
+                         (assq field (cdr entry)))
+                       (condition-type-all-fields type)))))))
+
+(define-syntax condition
+  (syntax-rules ()
+    ((condition (?type1 (?field1 ?value1) ...) ...)
+     (type-field-alist->condition
+      (list
+       (cons ?type1
+             (list (cons '?field1 ?value1) ...))
+       ...)))))
+
+(define (type-field-alist->condition type-field-alist)
+  (really-make-condition
+   (map (lambda (entry)
+          (cons (car entry)
+                (map (lambda (field)
+                       (or (assq field (cdr entry))
+                           (cons field
+                                 (type-field-alist-ref type-field-alist field))))
+                     (condition-type-all-fields (car entry)))))
+        type-field-alist)))
+
+(define (condition-types condition)
+  (map car (condition-type-field-alist condition)))
+
+(define (check-condition-type-field-alist the-type-field-alist)
+  (let loop ((type-field-alist the-type-field-alist))
+    (if (not (null? type-field-alist))
+        (let* ((entry (car type-field-alist))
+               (type (car entry))
+               (field-alist (cdr entry))
+               (fields (map car field-alist))
+               (all-fields (condition-type-all-fields type)))
+          (for-each (lambda (missing-field)
+                      (let ((supertype
+                             (condition-type-field-supertype type missing-field)))
+                        (if (not
+                             (any (lambda (entry)
+                                    (let ((type (car entry)))
+                                      (condition-subtype? type supertype)))
+                                  the-type-field-alist))
+                            (error "missing field in condition construction"
+                                   type
+                                   missing-field))))
+                    (lset-difference eq? all-fields fields))
+          (loop (cdr type-field-alist))))))
+
+(define &condition (really-make-condition-type '&condition
+                                               #f
+                                               '()
+                                               '()))
+
+(define-condition-type &message &condition
+  message-condition?
+  (message condition-message))
+
+(define-condition-type &serious &condition
+  serious-condition?)
+
+(define-condition-type &error &serious
+  error?)
+

References

+ + +

Copyright

+ +

Copyright (C) Richard Kelsey, Michael Sperber (2002). All Rights +Reserved.

+ +

+Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: +

+

+The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. +

+

+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +

+ +
+
Editor: Francisco Solsona
+ + diff --git a/doc/srfi-std/srfi-38.html b/doc/srfi-std/srfi-38.html new file mode 100644 index 0000000000..378fbd2816 --- /dev/null +++ b/doc/srfi-std/srfi-38.html @@ -0,0 +1,637 @@ + + + + SRFI 38: External Representation for Data With Shared Structure + + + + +

Title

+ +External Representation for Data With Shared Structure + +

Author

+ +Ray Dillinger + +

Status

+ +This SRFI is currently in ``final'' status. To see an explanation of +each status that a SRFI can hold, see here. +You can access +the discussion via + +the archive of the mailing list. + + + +

Abstract

+

+This SRFI proposes (write-with-shared-structure) and +(read-with-shared-structure), procedures for writing +and reading external representations of data containing shared +structure. These procedures implement a proposed standard +external notation for data containing shared structure. +

+

+This SRFI permits but does not require replacing the standard +(write) and (read) functions. These functions +may be implemented without the overhead in time and space required +to detect and specify shared structure. +

+

+An implementation conforms to this SRFI if it provides procedures +named (write-with-shared-structure) and +(read-with-shared-structure), which produce and read +the same notation as produced by the reference implementation. +It may also provide (read/ss) and (write/ss), +equivalent functions with shorter names. +

+ + +

Rationale

+ +

+ +R5RS scheme and IEEE scheme provide the procedure (write), +which prints machine-readable representations of lists and other +objects. However, the printed representation does not preserve +information about what parts of the structure are shared, and in the +case of self-referential objects the behavior of (write) +itself is undefined; it is permitted to go into an infinite loop or +invoke the dreaded curse of the nasal demons. + +

+

+ +For example, it is possible to have a list within which two or more +members are the same string (in the sense of (eq?)), but when +the list is written, there is not sufficient information in the +representation to recover the (eq?) relationship. When the +list is read back in, there will be two or more copies of the string +which are (eqv?) but not (eq?). + +

+

+As an example of the second problem, The results of evaluating +

+(begin (define a (cons 'val1 'val2))
+       (set-cdr! a a)
+       (write a))
+
+ +are undefined; in R5RS parlance, calling write on such a structure +"is an error", but not one that is necessarily +signalled. The routine is permitted to print a nonstandard notation +such as the one proposed in this standard or a different one, fail +silently, signal an error, go into an infinite loop, or make demons +fly out of your nose. Some of these results are unacceptable in some +cases. This SRFI hopes to provide a standard way of dealing with this +problem by providing a method of writing data which is guaranteed to +be well-behaved and predictable even on data containing shared +structures. + +

+ +

+ +The extended functionality described below in the implementation of +(write-with-shared-structure)is already present in the +(write) function of several major scheme implementations +(PLT, SISC, Chez, Bigloo, MIT scheme, and possibly others). + +

+ +

Specification

+

+ +

Formal Grammar of the New External Representation

+This SRFI creates an alternative external representation for data +written and read under (write/ss) and (read/ss). +It is identical to the grammar for external representation for data +written and read under (write) and (read) given in +section 7 of R5RS, except that the single production +
+
+<datum> --> <simple datum> | <compound datum> 
+
+
+Is replaced by the following five productions. +
+
+<datum> --> <defining datum> | <nondefining datum> | <defined datum>
+
+<defining datum> -->  #<indexnum>=<nondefining datum>
+
+<defined datum> --> #<indexnum>#
+
+<nondefining datum> --> <simple datum> | <compound datum> 
+
+<indexnum> --> <digit 10>+
+
+

+ +

New Procedures

+

+

+
+[[library procedure]] (write-with-shared-structure obj)
+[[library procedure]] (write-with-shared-structure obj port)
+[[library procedure]] (write-with-shared-structure obj port optarg)
+
+
+ +Writes a written representation of obj to the given port. Strings that +appear in the written representation are enclosed in doublequotes, and +within those strings backslash and doublequote characters are escaped +by backslashes. Character objects are written using the #\ +notation. + +

+ +Objects which denote locations rather than values (cons cells, +vectors, and non-zero-length strings in R5RS scheme; also mutable +objects, records, or containers if provided by the implementation), if +they appear at more than one point in the data being written, must be +preceded by "#N=" the first time they are written and +replaced by "#N#" all subsequent times they are written, +where N is a natural number used to identify that particular object. +If objects which denote locations occur only once in the structure, +then (write-with-shared-structure) must produce the same +external representation for those objects as (write). + +

+ +

+ +Write-with-shared-structure must terminate in finite time when writing +finite data. Write-with-shared-structure must produce a finite +representation when writing finite data. + +

+ +Write-with-shared-structure returns an unspecified value. The port +argument may be omitted, in which case it defaults to the value +returned by (current-output-port). The optarg argument may +also be omitted. If present, its effects on the output and return +value are unspecified but (write-with-shared-structure) must +still write a representation that can be read by +(read-with-shared-structure). Some implementations may wish to +use optarg to specify formatting conventions, numeric radixes, or +return values. The reference implementation ignores optarg. + +

+

+ +For example, the code +

+
+(begin (define a (cons 'val1 'val2))
+       (set-cdr! a a)
+       (write-with-shared-structure a))
+
+
+ +should produce the output #1=(val1 . #1#) . This shows a cons +cell whose cdr contains itself. + +

+ +

+
+[[library procedure]] (read-with-shared-structure)
+[[library procedure]] (read-with-shared-structure  port)
+
+
+ +(read-with-shared-structure) converts the external +representations of Scheme objects produced by +(write-with-shared-structure) into scheme objects. That is, +it is a parser for the nonterminal <datum> in the augmented +external representation grammar defined above. +(read-with-shared-structure) returns the next object parsable +from the given input port, updating port to point to the first +character past the end of the external representation of the object. + +

+ +If an end-of-file is encountered in the input before any characters +are found that can begin an object, then an end-of-file object is +returned. The port remains open, and further attempts to read it (by +(read-with-shared-structure) or (read) will also +return an end-of-file object. If an end of file is encountered after +the beginning of an object's external representation, but the external +representation is incomplete and therefore not parsable, an error is +signalled. + +

+ +The port argument may be omitted, in which case it defaults to the +value returned by (current-input-port). It is an error to +read from a closed port. + +

+ + + + +

Implementation

+ +

+ +The reference implementation of (write-with-shared-structure) +is based on an implementation provided by Al Petrofsky. If there are +any errors in it, I probably introduced them when I was adding support +for an optional port argument. The reference implementation of +(read-with-shared-structure) is the implementation +provided by Al Petrofsky. Both are used here with his generous +permission. + +

+ +

+ +Note that portability forces the reference implementation of +(write-with-shared-structure) to be O(N^2) but that if an +implementor tracks objects through additional fields hidden from R5RS +scheme, a more efficient implementation can be provided. + +

+

+ +If all the locations in your scheme are mutable and you don't do any +locking or multithreading, you can write an O(n) version that +destructively marks locations as it goes and then restores them all +when done. If locations are immutable, then there should be some +fixed ordering of them that you can use to make an O(log n) lookup +table, giving you an O(n log n) write-with-shared-structure. R5RS +does not give the programmer access to mutability information nor to +comparison of constant data's addresses, but both of these are trivial +operations if you have access to the system's internals. + +

+ + +
+;;; A printer that shows all sharing of substructures.  Uses the Common
+;;; Lisp print-circle notation: #n# refers to a previous substructure
+;;; labeled with #n=.   Takes O(n^2) time.
+
+
+  (define (write-with-shared-structure obj . optional-port)
+    (define (acons key val alist)
+      (cons (cons key val) alist))
+    (define outport (if (eq? '() optional-port)
+			(current-output-port)
+			(car optional-port)))
+    ;; We only track duplicates of pairs, vectors, and strings.  We
+    ;; ignore zero-length vectors and strings because r5rs doesn't
+    ;; guarantee that eq? treats them sanely (and they aren't very
+    ;; interesting anyway).
+
+    (define (interesting? obj)
+      (or (pair? obj)
+	  (and (vector? obj) (not (zero? (vector-length obj))))
+	  (and (string? obj) (not (zero? (string-length obj))))))
+    ;; (write-obj OBJ ALIST):
+    ;; ALIST has an entry for each interesting part of OBJ.  The
+    ;; associated value will be:
+    ;;  -- a number if the part has been given one,
+    ;;  -- #t if the part will need to be assigned a number but has not been yet,
+    ;;  -- #f if the part will not need a number.
+    ;; The cdr of ALIST's first element should be the most recently
+    ;; assigned number.
+    ;; Returns an alist with new shadowing entries for any parts that
+    ;; had numbers assigned.
+    (define (write-obj obj alist)
+      (define (write-interesting alist)
+	(cond ((pair? obj)
+	       (display "(" outport)
+	       (let write-cdr ((obj (cdr obj)) (alist (write-obj (car obj) alist)))
+		 (cond ((and (pair? obj) (not (cdr (assq obj alist))))
+			(display " " outport)
+			(write-cdr (cdr obj) (write-obj (car obj) alist)))
+		       ((null? obj)
+			(display ")" outport)
+			alist)
+		       (else
+			(display " . " outport)
+			(let ((alist (write-obj obj alist)))
+			  (display ")" outport)
+			  alist)))))
+	      ((vector? obj)
+	       (display "#(" outport)
+	       (let ((len (vector-length obj)))
+		 (let write-vec ((i 1) (alist (write-obj (vector-ref obj 0) alist)))
+		   (cond ((= i len) (display ")" outport) alist)
+			 (else (display " " outport)
+			       (write-vec (+ i 1)
+					  (write-obj (vector-ref obj i) alist)))))))
+	      ;; else it's a string
+	      (else (write obj outport) alist)))
+      (cond ((interesting? obj)
+	     (let ((val (cdr (assq obj alist))))
+	       (cond ((not val) (write-interesting alist))
+		     ((number? val) 
+		      (begin (display "#" outport)
+			     (write val outport)
+			     (display "#" outport) alist))
+		     (else
+		      (let ((n (+ 1 (cdar alist))))
+			(begin (display "#" outport)
+			       (write n outport) 
+			       (display "=" outport))
+			(write-interesting (acons obj n alist)))))))
+	    (else (write obj outport) alist)))
+
+    ;; Scan computes the initial value of the alist, which maps each
+    ;; interesting part of the object to #t if it occurs multiple times,
+    ;; #f if only once.
+    (define (scan obj alist)
+      (cond ((not (interesting? obj)) alist)
+	    ((assq obj alist)
+             => (lambda (p) (if (cdr p) alist (acons obj #t alist))))
+	    (else
+	     (let ((alist (acons obj #f alist)))
+	       (cond ((pair? obj) (scan (car obj) (scan (cdr obj) alist)))
+		     ((vector? obj)
+		      (let ((len (vector-length obj)))
+			(do ((i 0 (+ 1 i))
+			     (alist alist (scan (vector-ref obj i) alist)))
+			    ((= i len) alist))))
+		     (else alist))))))
+    (write-obj obj (acons 'dummy 0 (scan obj '())))
+    ;; We don't want to return the big alist that write-obj just returned.
+    (if #f #f))
+
+
+
+
+
+(define (read-with-shared-structure . optional-port)
+  (define port
+    (if (null? optional-port) (current-input-port) (car optional-port)))
+
+  (define (read-char*) (read-char port))
+  (define (peek-char*) (peek-char port))
+
+  (define (looking-at? c)
+    (eqv? c (peek-char*)))
+
+  (define (delimiter? c)
+    (case c
+      ((#\( #\) #\" #\;) #t)
+      (else (or (eof-object? c)
+		(char-whitespace? c)))))
+
+  (define (not-delimiter? c) (not (delimiter? c)))
+
+  (define (eat-intertoken-space)
+    (define c (peek-char*))
+    (cond ((eof-object? c))
+	  ((char-whitespace? c) (read-char*) (eat-intertoken-space))
+	  ((char=? c #\;)
+	   (do ((c (read-char*) (read-char*)))
+	       ((or (eof-object? c) (char=? c #\newline))))
+	   (eat-intertoken-space))))
+
+  (define (read-string)
+    (read-char*)
+    (let read-it ((chars '()))
+      (let ((c (read-char*)))
+	(if (eof-object? c)
+	    (error "EOF inside a string")
+	    (case c
+	      ((#\") (list->string (reverse chars)))
+	      ((#\\) (read-it (cons (read-char*) chars)))
+	      (else (read-it (cons c chars))))))))
+
+  ;; reads chars that match PRED and returns them as a string.
+  (define (read-some-chars pred)
+    (let iter ((chars '()))
+      (let ((c (peek-char*)))
+	(if (or (eof-object? c) (not (pred c)))
+	    (list->string (reverse chars))
+	    (iter (cons (read-char*) chars))))))
+
+  ;; reads a character after the #\ has been read.
+  (define (read-character)
+    (let ((c (peek-char*)))
+      (cond ((eof-object? c) (error "EOF inside a character"))
+	    ((char-alphabetic? c)
+	     (let ((name (read-some-chars char-alphabetic?)))
+	       (cond ((= 1 (string-length name)) (string-ref name 0))
+		     ((string-ci=? name "space") #\space)
+		     ((string-ci=? name "newline") #\newline)
+		     (else (error "Unknown named character: " name)))))
+	    (else (read-char*)))))
+
+  (define (read-number first-char)
+    (let ((str (string-append (string first-char)
+			      (read-some-chars not-delimiter?))))
+      (or (string->number str)
+	  (error "Malformed number: " str))))
+
+  (define char-standard-case
+    (if (char=? #\a (string-ref (symbol->string 'a) 0))
+	char-downcase
+	char-upcase))
+
+  (define (string-standard-case str)
+    (let* ((len (string-length str))
+	   (new (make-string len)))
+      (do ((i 0 (+ i 1)))
+	  ((= i len) new)
+	(string-set! new i (char-standard-case (string-ref str i))))))
+
+  (define (read-identifier)
+    (string->symbol (string-standard-case (read-some-chars not-delimiter?))))
+
+  (define (read-part-spec)
+    (let ((n (string->number (read-some-chars char-numeric?))))
+      (let ((c (read-char*)))
+	(case c
+	  ((#\=) (cons 'decl n))
+	  ((#\#) (cons 'use n))
+	  (else (error "Malformed shared part specifier"))))))
+
+  ;; Tokens: strings, characters, numbers, booleans, and
+  ;; identifiers/symbols are represented as themselves.
+  ;; Single-character tokens are represented as (CHAR), the
+  ;; two-character tokens #( and ,@ become (#\#) and (#\@).
+  ;; #NN= and #NN# become (decl . NN) and (use . NN).
+  (define (read-optional-token)
+    (eat-intertoken-space)
+    (let ((c (peek-char*)))
+      (case c
+	((#\( #\) #\' #\`) (read-char*) (list c))
+	((#\,)
+	 (read-char*)
+	 (if (looking-at? #\@)
+	     (begin (read-char*) '(#\@))
+	     '(#\,)))
+	((#\") (read-string))
+	((#\.)
+	 (read-char*)
+	 (cond ((delimiter? (peek-char*)) '(#\.))
+	       ((not (looking-at? #\.)) (read-number #\.))
+	       ((begin (read-char*) (looking-at? #\.)) (read-char*) '...)
+	       (else (error "Malformed token starting with \"..\""))))
+	((#\+) (read-char*) (if (delimiter? (peek-char*)) '+ (read-number c)))
+	((#\-) (read-char*) (if (delimiter? (peek-char*)) '- (read-number c)))
+	((#\#)
+	 (read-char*)
+	 (let ((c (peek-char*)))
+	   (case c
+	     ((#\() (read-char*) '(#\#))
+	     ((#\\) (read-char*) (read-character))
+	     ((#\t #\T) (read-char*) #t)
+	     ((#\f #\F) (read-char*) #f)
+	     (else (cond ((eof-object? c) (error "EOF inside a # token"))
+			 ((char-numeric? c) (read-part-spec))
+			 (else (read-number #\#)))))))
+	(else (cond ((eof-object? c) c)
+		    ((char-numeric? c) (read-char*) (read-number c))
+		    (else (read-identifier)))))))
+
+  (define (read-token)
+    (let ((tok (read-optional-token)))
+      (if (eof-object? tok)
+	  (error "EOF where token was required")
+	  tok)))
+
+  ;; Parts-alist maps the number of each part to a thunk that returns the part.
+  (define parts-alist '())
+
+  (define (add-part-to-alist! n thunk)
+    (set! parts-alist (cons (cons n thunk) parts-alist)))
+
+  ;; Read-object returns a datum that may contain some thunks, which
+  ;; need to be replaced with their return values.
+  (define (read-object)
+    (finish-reading-object (read-token)))
+
+  ;; Like read-object, but may return EOF.
+  (define (read-optional-object)
+    (finish-reading-object (read-optional-token)))
+
+  (define (finish-reading-object first-token)
+    (if (not (pair? first-token))
+	first-token
+	(if (char? (car first-token))
+	    (case (car first-token)
+	      ((#\() (read-list-tail))
+	      ((#\#) (list->vector (read-list-tail)))
+	      ((#\. #\)) (error (string-append "Unexpected \"" first-token "\"")))
+	      (else
+	       (list (caadr (assv (car first-token)
+				  '((#\' 'x) (#\, ,x) (#\` `x) (#\@ ,@x))))
+		     (read-object))))
+	    ;; We need to specially handle chains of declarations in
+	    ;; order to allow #1=#2=x and #1=(#2=#1#) and not to allow
+	    ;; #1=#2=#1# nor #1=#2=#1=x.
+	    (let ((starting-alist parts-alist))
+	      (let read-decls ((token first-token))
+		(if (and (pair? token) (symbol? (car token)))
+		    (let ((n (cdr token)))
+		      (case (car token)
+			((use)
+			 ;; To use a part, it must have been
+			 ;; declared before this chain started.
+			 (cond ((assv n starting-alist) => cdr)
+			       (else (error "Use of undeclared part " n))))
+			((decl)
+			 (if (assv n parts-alist)
+			     (error "Double declaration of part " n))
+			 ;; Letrec enables us to make deferred
+			 ;; references to an object before it exists.
+			 (letrec ((obj (begin
+					 (add-part-to-alist! n (lambda () obj))
+					 (read-decls (read-token)))))
+			   obj))))
+		    (finish-reading-object token)))))))
+
+  (define (read-list-tail)
+    (let ((token (read-token)))
+      (if (not (pair? token))
+	  (cons token (read-list-tail))
+	  (case (car token)
+	    ((#\)) '())
+	    ((#\.) (let* ((obj (read-object))
+			  (tok (read-token)))
+		     (if (and (pair? tok) (char=? #\) (car tok)))
+			 obj
+			 (error "Extra junk after a dot"))))
+	    (else (let ((obj (finish-reading-object token)))
+		    (cons obj (read-list-tail))))))))
+
+  ;; Unthunk.
+  ;; To deference a part that was declared using another part,
+  ;; e.g. #2=#1#, may require multiple dethunkings.  We were careful
+  ;; in finish-reading-object to ensure that this won't loop forever:
+  (define (unthunk thunk)
+    (let ((x (thunk)))
+      (if (procedure? x) (unthunk x) x)))
+
+  (let ((obj (read-optional-object)))
+    (let fill-in-parts ((obj obj))
+      (cond ((pair? obj)
+	     (if (procedure? (car obj))
+		 (set-car! obj (unthunk (car obj)))
+		 (fill-in-parts (car obj)))
+	     (if (procedure? (cdr obj))
+		 (set-cdr! obj (unthunk (cdr obj)))
+		 (fill-in-parts (cdr obj))))
+	    ((vector? obj)
+	     (let ((len (vector-length obj)))
+	       (do ((i 0 (+ i 1)))
+		   ((= i len))
+		 (let ((elt (vector-ref obj i)))
+		   (if (procedure? elt)
+		       (vector-set! obj i (unthunk elt))
+		       (fill-in-parts elt))))))))
+    obj))
+
+
+ + +

Copyright

+

Copyright (C) Ray Dillinger 2003. All Rights Reserved.

+ +

+Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: +

+

+The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. +

+

+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +

+ +
+
Editor: David Rush
+ + +Last modified: Wed Apr 2 19:58:58 BST 2003 + + + + diff --git a/doc/srfi-std/srfi-39.html b/doc/srfi-std/srfi-39.html new file mode 100644 index 0000000000..efd33ed205 --- /dev/null +++ b/doc/srfi-std/srfi-39.html @@ -0,0 +1,355 @@ + + + + SRFI 39: Parameter objects + + + + +

Title

+ +SRFI 39: Parameter objects + +

Author

+ +Marc Feeley + +

Status

+ +This SRFI is currently in ``final'' status. To see an explanation of +each status that a SRFI can hold, see here. +You can access +previous messages via + +the archive of the mailing list. + + + + +

Abstract

+ +

+This SRFI defines parameter objects, the procedure +make-parameter to +create parameter objects and the parameterize special +form to dynamically bind parameter objects. In the dynamic +environment, each parameter object is bound to a cell containing the +value of the parameter. When a procedure is called the called +procedure inherits the dynamic environment from the caller. The +parameterize special form allows the binding of a +parameter object to be changed for the dynamic extent of its body. +

+ +

Rationale

+ +

+The dynamic environment is the structure which allows +the system to find the value returned by the R5RS procedures +current-input-port and current-output-port. +The R5RS procedures with-input-from-file and +with-output-to-file extend the dynamic environment to +produce a new dynamic environment which is in effect for the dynamic +extent of the call to the thunk passed as their last argument. These +procedures are essentially special purpose dynamic binding operations +on hidden dynamic variables (one for current-input-port +and one for current-output-port). The purpose of this +SRFI is to generalize this dynamic binding mechanism (which exists in +all R5RS compliant systems) to allow the user to introduce new dynamic +variables and dynamically bind them. +

+ +

+General dynamic binding mechanisms exist in several implementations of +Scheme under various names, including "fluid" variables and parameter +objects. The parameter objects specified in this SRFI are compatible +with the semantics of all implementations of Scheme we know which +currently support parameter objects (in the sense that it is possible +to implement this SRFI so that old code works the same as before). We +believe Chez-Scheme was the first implementation of Scheme to have +used parameter objects. +

+ +

+In the presence of threads, the dynamic binding mechanism does not +behave the same way in all implementations of Scheme supporting +dynamic binding. The issue is the relationship between the dynamic +environments of the parent and child threads when a thread is created. +In Scheme 48 the child gets a fresh dynamic environment where +(typically but not necessarily) all the bindings are to their initial +value. In MzScheme and Gambit-C the child is given a dynamic +environment inherited from the parent. In this inherited dynamic +environment the dynamic variables have the same values as the parent's +dynamic environment. However, in MzScheme the cells bound to the +dynamic variables in the child are distinct from those of the parent +(i.e. an assignment of a value to a dynamic variable is not visible in +the other thread). In Gambit-C the child and parent dynamic +environment share the same cells (i.e. an assignment of a value to a +dynamic variable is visible in the other thread). Note that in the +absence of assignment to dynamic variables the MzScheme and Gambit-C +approaches are equivalent. +

+ +

+Given that there are semantic differences in the presence of threads +and that there are valid reasons for choosing each semantics, this +SRFI does not specify the semantics of parameter objects in the +presence of threads. It is left to the implementation and other SRFIs +which extend this SRFI to specify the interaction between parameter +objects and threads. +

+ +

Specification

+ +

+The dynamic environment is composed of two parts: the local +dynamic environment and the global dynamic environment. +The global dynamic environment is used to lookup parameter objects +that can't be found in the local dynamic environment. When parameter +objects are created, their initial binding is put in the global +dynamic environment (by mutation). The local dynamic environment is +only extended by the parameterize form. +

+ +

+Parameter objects are created with the +make-parameter procedure which takes one or two +arguments. The second argument is a one argument conversion +procedure. If only one argument is passed to +make-parameter the identity function is used as a +conversion procedure. The global dynamic environment is updated to +associate the parameter object to a new cell. The initial content of +the cell is the result of applying the conversion procedure to the +first argument of make-parameter. +

+ +

+A parameter object is a procedure which accepts zero or one +argument. The cell bound to a particular parameter object in the +dynamic environment is accessed by calling the parameter object. When +no argument is passed, the content of the cell is returned. When one +argument is passed the content of the cell is updated with the result +of applying the parameter object's conversion procedure to the +argument. +

+ +

+The parameterize special form, when given a parameter +object and a value, binds for the dynamic extent of its body the +parameter object to a new cell. The initial content of the cell is +the result of applying the parameter object's conversion procedure to +the value. The parameterize special form behaves +analogously to let when binding more than one parameter +object (that is the order of evaluation is unspecified and the new +bindings are only visible in the body of the parameterize +special form). +

+ +

+Note that the conversion procedure can be used for guaranteeing the +type of the parameter object's binding and/or to perform some +conversion of the value. +

+ +

+Because it is possible to implement the R5RS procedures +current-input-port and current-output-port +as parameter objects and this offers added functionnality, it is +required by this SRFI that they be implemented as parameter objects +created with make-parameter. +

+ +

Procedures and syntax

+ +
+ +
+(make-parameter init [converter])                     ;procedure
+
+ +

+Returns a new parameter object which is bound in the global dynamic +environment to a cell containing the value returned by the call +(converter init). If the conversion +procedure converter is not specified the identity function is +used instead. +

+ +

+The parameter object is a procedure which accepts zero or one +argument. When it is called with no argument, the content of the cell +bound to this parameter object in the current dynamic environment is +returned. When it is called with one argument, the content of the +cell bound to this parameter object in the current dynamic environment +is set to the result of the call (converter +arg), where arg is the argument passed to the +parameter object, and an unspecified value is returned. +

+ +
+    (define radix
+      (make-parameter 10))
+
+    (define write-shared
+      (make-parameter
+        #f
+        (lambda (x)
+          (if (boolean? x)
+              x
+              (error "only booleans are accepted by write-shared")))))
+
+    (radix)           ==>  10
+    (radix 2)
+    (radix)           ==>  2
+    (write-shared 0)  gives an error
+
+    (define prompt
+      (make-parameter
+        123
+        (lambda (x)
+          (if (string? x)
+              x
+              (with-output-to-string (lambda () (write x)))))))
+
+    (prompt)       ==>  "123"
+    (prompt ">")
+    (prompt)       ==>  ">"
+
+ +

+

+(parameterize ((expr1 expr2) ...) <body>)             ;syntax
+
+ +

+ The expressions expr1 and expr2 are evaluated in an + unspecified order. The value of the expr1 expressions must + be parameter objects. For each expr1 expression and in an + unspecified order, the local dynamic environment is extended with + a binding of the parameter object expr1 to a new cell whose + content is the result of the call (converter + val), where val is the value of expr2 + and converter is the conversion procedure of the parameter + object. The resulting dynamic environment is then used for the + evaluation of <body> (which refers to the R5RS grammar + nonterminal of that name). The result(s) of the + parameterize form are the result(s) of the + <body>. +

+ +
+    (radix)                                              ==>  2
+    (parameterize ((radix 16)) (radix))                  ==>  16
+    (radix)                                              ==>  2
+
+    (define (f n) (number->string n (radix)))
+
+    (f 10)                                               ==>  "1010"
+    (parameterize ((radix 8)) (f 10))                    ==>  "12"
+    (parameterize ((radix 8) (prompt (f 10))) (prompt))  ==>  "1010"
+
+ +

Implementation

+ +

+The following implementation uses association lists to represent local +dynamic environments. The global dynamic environment binding is +stored in the parameter object itself. Since we are assuming that +there is a single thread, the current local dynamic environment can be +bound to a global variable, dynamic-env-local. Mutations +of this variable are wrapped in a dynamic-wind so that +the local dynamic environment returns to its previous value when +control exits the body of the parameterize. +

+ +
+    (define make-parameter
+      (lambda (init . conv)
+        (let ((converter
+               (if (null? conv) (lambda (x) x) (car conv))))
+          (let ((global-cell
+                 (cons #f (converter init))))
+            (letrec ((parameter
+                      (lambda new-val
+                        (let ((cell (dynamic-lookup parameter global-cell)))
+                          (cond ((null? new-val)
+                                 (cdr cell))
+                                ((null? (cdr new-val))
+                                 (set-cdr! cell (converter (car new-val))))
+                                (else ; this case is needed for parameterize
+                                 (converter (car new-val))))))))
+              (set-car! global-cell parameter)
+              parameter)))))
+
+    (define-syntax parameterize
+      (syntax-rules ()
+        ((parameterize ((expr1 expr2) ...) body ...)
+         (dynamic-bind (list expr1 ...)
+                       (list expr2 ...)
+                       (lambda () body ...)))))
+
+    (define dynamic-bind
+      (lambda (parameters values body)
+        (let* ((old-local
+                (dynamic-env-local-get))
+               (new-cells
+                (map (lambda (parameter value)
+                       (cons parameter (parameter value #f)))
+                     parameters
+                     values))
+               (new-local
+                (append new-cells old-local)))
+          (dynamic-wind
+            (lambda () (dynamic-env-local-set! new-local))
+            body
+            (lambda () (dynamic-env-local-set! old-local))))))
+
+    (define dynamic-lookup
+      (lambda (parameter global-cell)
+        (or (assq parameter (dynamic-env-local-get))
+            global-cell)))
+
+    (define dynamic-env-local '())
+
+    (define dynamic-env-local-get
+      (lambda () dynamic-env-local))
+
+    (define dynamic-env-local-set!
+      (lambda (new-env) (set! dynamic-env-local new-env)))
+
+ +

Copyright

+

Copyright (C) Marc Feeley 2002. All Rights Reserved.

+ +

+Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: +

+

+The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. +

+

+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +

+ + +
+
Editor: Mike Sperber
+ + diff --git a/doc/srfi-std/srfi-4.html b/doc/srfi-std/srfi-4.html new file mode 100644 index 0000000000..00f474d9dc --- /dev/null +++ b/doc/srfi-std/srfi-4.html @@ -0,0 +1,368 @@ + + + + SRFI 4: Homogeneous numeric vector datatypes + + + + +

Title

+ +SRFI-4: Homogeneous numeric vector datatypes + +

Author

+ +Marc Feeley + +

Status

+This SRFI is currently in ``final'' status. To see an explanation of +each status that a SRFI can hold, see here. +You can access previous messages via the archive of the mailing list. +

+ +

Abstract

+ +This SRFI describes a set of datatypes for vectors whose elements are +of the same numeric type (signed or unsigned exact integer or inexact +real of a given precision). These datatypes support operations +analogous to the Scheme vector type, but they are distinct datatypes. +An external representation is specified which must be supported by the +read and write procedures and by the program +parser (i.e. programs can contain references to literal homogeneous +vectors). + +

Rationale

+ +Like lists, Scheme vectors are a heterogeneous datatype which impose +no restriction on the type of the elements. This generality is not +needed for applications where all the elements are of the same type. +The use of Scheme vectors is not ideal for such applications because, +in the absence of a compiler with a fancy static analysis, the +representation will typically use some form of boxing of the elements +which means low space efficiency and slower access to the elements. +Moreover, homogeneous vectors are convenient for interfacing with +low-level libraries (e.g. binary block I/O) and to interface with +foreign languages which support homogeneous vectors. Finally, the use +of homogeneous vectors allows certain errors to be caught earlier. + +

+ +This SRFI specifies a set of homogeneous vector datatypes which cover +the most practical case, that is where the type of the elements is +numeric (exact integer or inexact real) and the precision and +representation is efficiently implemented on the hardware of most +current computer architectures (8, 16, 32 and 64 bit integers, either +signed or unsigned, and 32 and 64 bit floating point numbers). + +

Specification

+ +There are 8 datatypes of exact integer homogeneous vectors (which will +be called integer vectors): + + + + + + + + + + + + + + + + + + + + +
datatype type of elements +
s8vector signed exact integer in the range -(2^7) to (2^7)-1 +
u8vector unsigned exact integer in the range 0 to (2^8)-1 +
s16vectorsigned exact integer in the range -(2^15) to (2^15)-1 +
u16vectorunsigned exact integer in the range 0 to (2^16)-1 +
s32vectorsigned exact integer in the range -(2^31) to (2^31)-1 +
u32vectorunsigned exact integer in the range 0 to (2^32)-1 +
s64vectorsigned exact integer in the range -(2^63) to (2^63)-1 +
u64vectorunsigned exact integer in the range 0 to (2^64)-1 +
+ +

+ +There are 2 datatypes of inexact real homogeneous vectors (which will +be called float vectors): + + + + + + + + +
datatype type of elements +
f32vector inexact real +
f64vector inexact real +
+ +

+ +The only difference between the two float vector types is that +f64vectors preserve at least as much precision as +f32vectors (see the implementation section for details). + +

+ +A Scheme system that conforms to this SRFI does not have to support +all of these homogeneous vector datatypes. However, a Scheme system +must support f32vectors and f64vectors if it +supports Scheme inexact reals (of any precision). A Scheme system +must support a particular integer vector datatype if the system's +exact integer datatype contains all the values that can be stored in +such an integer vector. Thus a Scheme system with bignum support must +implement all the integer vector datatypes and a Scheme system may +only support s8vectors, u8vectors, +s16vectors and u16vectors if it only +supports small integers in the range -(2^29) to (2^29)-1 (which would +be the case if they are represented as 32 bit fixnums with two bits +for tag). Note that it is possible to test which numeric datatypes +the Scheme system supports by calling the +string->number procedure (e.g. +(string->number "0.0") returns #f if the +Scheme system does not support inexact reals). + +

+ +Each homogeneous vector datatype has an external representation which +is supported by the read and write +procedures and by the program parser. Each datatype also has a set of +associated predefined procedures analogous to those available for +Scheme's heterogeneous vectors. + +

+ +For each value of TAG in { +s8, u8, +s16, u16, +s32, u32, +s64, u64, +f32, f64 +}, if the datatype TAGvector is supported, then + +

+ +
    + +
  1. the external representation of instances of the datatype +TAGvector is #TAG( ...elements... ). + +

    + +For example, #u8(0 #e1e2 #xff) is an +u8vector of length 3 containing 0, 100 and 255; +#f64(-1.5) is an f64vector of length 1 +containing -1.5. + +

    + +Note that the syntax for float vectors conflicts with Standard Scheme +which parses #f32() as 3 objects: #f, +32 and (). For this reason, conformance to +this SRFI implies this minor nonconformance to Standard Scheme. + +

    + +This external representation is also available in program source code. +For example, (set! x '#u8(1 2 3)) will set x +to the object #u8(1 2 3). Literal homogeneous vectors +must be quoted just like heterogeneous vectors must be. Homogeneous +vectors can appear in quasiquotations but must not contain +unquote or unquote-splicing forms +(i.e. `(,x #u8(1 2)) is legal but `#u8(1 ,x +2) is not). This restriction is to accomodate the many Scheme +systems that use the read procedure to parse programs. + +

    + +
  2. the following predefined procedures are available: + +
      +
    1. (TAGvector? obj) +
    2. (make-TAGvector n [ TAGvalue ]) +
    3. (TAGvector TAGvalue...) +
    4. (TAGvector-length TAGvect) +
    5. (TAGvector-ref TAGvect i) +
    6. (TAGvector-set! TAGvect i TAGvalue) +
    7. (TAGvector->list TAGvect) +
    8. (list->TAGvector TAGlist) +
    + +where obj is any Scheme object, n is a +nonnegative exact integer, i is a nonnegative exact +integer less than the length of the vector, TAGvect is an +instance of the TAGvector datatype, TAGvalue +is a number of the type acceptable for elements of the +TAGvector datatype, and TAGlist is a proper +list of numbers of the type acceptable for elements of the +TAGvector datatype. + +

    + +It is an error if TAGvalue is not the same type as the +elements of the TAGvector datatype (for example if an +exact integer is passed to f64vector). If the fill +value is not specified, the content of the vector is unspecified +but individual elements of the vector are guaranteed to be in the +range of values permitted for that type of vector. + +
+ +

Implementation

+ +The homogeneous vector datatypes described here suggest a concrete +implementation as a sequence of 8, 16, 32 or 64 bit elements, using +two's complement representation for the signed exact integers, and +single and double precision IEEE-754 floating point representation for +the inexact reals. Although this is a practical implementation on +many modern byte adressed machines, a different implementation is +possible for machines which don't support these concrete numeric types +(the CRAY-T90 for example does not have a 32 bit floating point +representation and the 64 bit floating point representation does not +conform to IEEE-754, so both the f32vector and +f64vector datatypes could be represented the same way +with 64 bit floating point numbers). + +

+ +A portable implementation of the homogeneous vector predefined +procedures can be based on Scheme's heterogeneous vectors. +Here is for example an implementation of s8vectors +which is exempt of error checking: + +
+(define s8vector? #f)
+(define make-s8vector #f)
+(define s8vector #f)
+(define s8vector-length #f)
+(define s8vector-ref #f)
+(define s8vector-set! #f)
+(define s8vector->list #f)
+(define list->s8vector #f)
+
+(let ((orig-vector? vector?)
+      (orig-make-vector make-vector)
+      (orig-vector vector)
+      (orig-vector-length vector-length)
+      (orig-vector-ref vector-ref)
+      (orig-vector-set! vector-set!)
+      (orig-vector->list vector->list)
+      (orig-list->vector list->vector)
+      (orig-> >)
+      (orig-eq? eq?)
+      (orig-+ +)
+      (orig-null? null?)
+      (orig-cons cons)
+      (orig-car car)
+      (orig-cdr cdr)
+      (orig-not not)
+      (tag (list 's8)))
+
+  (set! s8vector?
+    (lambda (obj)
+      (and (orig-vector? obj)
+           (orig-> (orig-vector-length obj) 0)
+           (orig-eq? (orig-vector-ref obj 0) tag))))
+
+  (set! make-s8vector
+    (lambda (n . opt-fill)
+      (let ((v (orig-make-vector
+                 (orig-+ n 1)
+                 (if (orig-null? opt-fill) 123 (orig-car opt-fill)))))
+        (orig-vector-set! v 0 tag)
+        v)))
+
+  (set! s8vector
+    (lambda s8list
+      (orig-list->vector (orig-cons tag s8list))))
+
+  (set! s8vector-length
+    (lambda (s8vect)
+      (orig-+ (orig-vector-length s8vect) -1)))
+
+  (set! s8vector-ref
+    (lambda (s8vect i)
+      (orig-vector-ref s8vect (orig-+ i 1))))
+
+  (set! s8vector-set!
+    (lambda (s8vect i s8value)
+      (orig-vector-set! s8vect (orig-+ i 1) s8value)))
+
+  (set! s8vector->list
+    (lambda (s8vect)
+      (orig-cdr (orig-vector->list s8vect))))
+
+  (set! list->s8vector
+    (lambda (s8list)
+      (orig-list->vector (orig-cons tag s8list))))
+
+  (set! vector?
+    (lambda (obj)
+      (and (orig-vector? obj)
+           (orig-not (and (orig-> (orig-vector-length obj) 0)
+                          (orig-eq? (orig-vector-ref obj 0) tag)))))))
+
+ +

+ +The Scheme system's read and write +procedures and the program parser also need to be extended to handle +the homogeneous vector external representations. The implementation +is very similar to heterogeneous vectors except that read +and the program parser must recognize the prefixes #s8, +#f32, etc. (this can be done by checking if the sharp +sign is followed by a character that can start a symbol, and if this +is the case, parse a symbol and check if it is t, +f, s8, f32, and so on, and in +the case of a homogeneous vector prefix, check if the next character +is an opening parenthesis). + +

Copyright

+Copyright (C) Marc Feeley (1999). All Rights Reserved. +

+Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: +

+

+The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. +

+

+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +

+ + +
+
Editor: + Shriram Krishnamurthi
+ + + diff --git a/doc/srfi-std/srfi-40.html b/doc/srfi-std/srfi-40.html new file mode 100644 index 0000000000..902fba7e8a --- /dev/null +++ b/doc/srfi-std/srfi-40.html @@ -0,0 +1,1116 @@ + + + + SRFI 40: A Library of Streams + + + + +

Title

+ +SRFI 40: A Library of Streams + +

Author

+ +Philip L. Bewig + +

Status

+ +

+This SRFI is currently in ``final'' status. To see an explanation of each status that a SRFI can hold, see here. +To comments +this SRFI, please mail to + +srfi-40@srfi.schemers.org. +See +instructions here to subscribe to the list. You can access +the discussion via + +the archive of the mailing list. +You can access +post-finalization messages via + +the archive of the mailing list. +

+ +
  • Received: 2003/02/03 +
  • Draft: 2003/02/03-2003/04/03 +
  • Revised: 2003/08/02 +
  • Revised: 2003/12/23 +
  • Final: 2004/08/22 + + +

    Abstract

    + +

    +Along with higher-order functions, one of the hallmarks of functional +programming is lazy evaluation. A primary manifestation of lazy +evaluation is lazy lists, generally called streams by Scheme +programmers, where evaluation of a list element is delayed until its +value is needed. +

    + +

    +The literature on lazy evaluation distinguishes two styles of +laziness, called even and odd. Odd style streams are ubiquitous among +Scheme programs and can be easily encoded with the Scheme primitives +delay and force defined in R5RS. However, the even style delays +evaluation in a manner closer to that of traditional lazy languages +such as Haskell and avoids an "off by one" error that is symptomatic +of the odd style. +

    + +

    +This SRFI defines the stream data type in the even style, some +essential procedures and syntax that operate on streams, and motivates +our choice of the even style. A companion SRFI 41 Stream Library +provides additional procedures and syntax which make for more +convenient processing of streams and shows several examples of their +use. +

    + +

    Rationale

    + +

    +Two of the defining characteristics of functional programming +languages are higher-order functions, which provide a powerful tool to +allow programmers to abstract data representations away from an +underlying concrete implementation, and lazy evaluation, which allows +programmers to modularize a program and recombine the pieces in useful +ways. Scheme provides higher-order functions through its lambda +keyword and lazy evaluation through its delay keyword. A primary +manifestation of lazy evaluation is lazy lists, generally called +streams by Scheme programmers, where evaluation of a list element is +delayed until its value is needed. Streams can be used, among other +things, to compute with infinities, conveniently process simulations, +program with coroutines, and reduce the number of passes over data. +This library defines a minimal set of functions and syntax for +programming with streams. +

    + +

    +Scheme has a long tradition of computing with streams. The great +computer science textbook Structure and Interpretation +of Computer Programs, uses streams extensively. +The example given +in R5RS makes use of streams to integrate systems of differential +equations using the method of Runge-Kutta. MIT Scheme, the original +implementation of Scheme, provides streams natively. Scheme and the Art of Programming, +discusses streams. +Some Scheme-like languages also have traditions of using streams: +Winston and Horn, in their classic Lisp textbook, discuss streams, and +so does Larry Paulson in his text on +ML. Streams are an important and useful data structure. +

    + +

    +Basically, a stream is much like a list, and can either be null or can +consist of an object (the stream element) followed by another stream; +the difference to a list is that elements aren't evaluated until they +are accessed. All the streams mentioned above use the same underlying +representation, with the null stream represented by '() +and stream pairs constructed by (cons car (delay cdr)), +which must be implemented as syntax. These streams are known as +head-strict, because the head of the stream is always computed, +whether or not it is needed. +

    + +

    +Streams are the central data type -- just as arrays are for most +imperative languages and lists are for Lisp and Scheme -- for the +"pure" functional languages Miranda and Haskell. But those streams +are subtly different from the traditional Scheme streams of SICP et +al. The difference is at the head of the stream, where Miranda and +Haskell provide streams that are fully lazy, with even the head of the +stream not computed until it is needed. We'll see in a moment the +operational difference between the two types of streams. +

    + +

    +Philip Wadler, Walid Taha, and David MacQueen, in their paper "How to add laziness to a strict language +without even being odd", describe how they added streams to the +SML/NJ compiler. They discuss two kinds of streams: odd streams, as +in SICP et al, and even streams, as in Haskell; the names odd and even +refer to the parity of the number of constructors (delay, +cons, nil) used to represent the stream. +Here are the first two figures from their paper, rewritten in Scheme: +

    + + + +
    +
    ;;; FIGURE 1 -- ODD                 
    +				    
    +(define nil1 '())                   
    +				    
    +(define (nil1? strm)                
    +  (null? strm))                     
    +				    
    +(define-syntax cons1                
    +  (syntax-rules ()                  
    +    ((cons1 obj strm)               
    +      (cons obj (delay strm)))))    
    +				    
    +(define (car1 strm)                 
    +  (car strm))                       
    +				    
    +(define (cdr1 strm)                 
    +  (force (cdr strm)))               
    +				    
    +(define (map1 func strm)            
    +                                    
    +  (if (nil1? strm)                  
    +    nil1                            
    +    (cons1                          
    +      (func (car1 strm))            
    +      (map1 func (cdr1 strm)))))    
    +				    
    +(define (countdown1 n)              
    +                                    
    +  (cons1 n (countdown1 (- n 1))))   
    +				    
    +(define (cutoff1 n strm)            
    +  (cond                             
    +    ((zero? n) '())                 
    +    ((nil1? strm) '())              
    +    (else                           
    +      (cons                         
    +        (car1 strm)                 
    +        (cutoff1 (- n 1)             
    +                 (cdr1 strm))))))    
    +
    +
    ;;; FIGURE 2 -- EVEN
    +
    +(define nil2 (delay '()))
    +
    +(define (nil2? strm)
    +  (null? (force strm)))
    +
    +(define-syntax cons2
    +  (syntax-rules ()
    +    ((cons2 obj strm)
    +     (delay (cons obj strm)))))
    +
    +(define (car2 strm)
    +  (car (force strm)))
    +
    +(define (cdr2 strm)
    +  (cdr (force strm)))
    +
    +(define (map2 func strm)
    +  (delay (force
    +    (if (nil2? strm)
    +      nil2
    +      (cons2
    +        (func (car2 strm))
    +        (map2 func (cdr2 strm)))))))
    +
    +(define (countdown2 n)
    +  (delay (force
    +    (cons2 n (countdown2 (- n 1))))))
    +
    +(define (cutoff2 n strm)
    +  (cond
    +    ((zero? n) '())
    +    ((nil2? strm) '())
    +    (else
    +      (cons
    +        (car2 strm)
    +        (cutoff2 (- n 1)
    +                 (cdr2 strm))))))
    +
    + +

    +It is easy to see the operational difference between the two kinds of streams, using +an example adapted from the paper: +

    + + + + + + +
    +
    > (define (12div n) (/ 12 n))       
    +> (cutoff1 4                        
    +    (map1 12div (countdown1 4)))    
    +error: divide by zero               
    +
    +
    +
    > (define (12div n) (/ 12 n))
    +> (cutoff2 4
    +    (map2 12div (countdown2 4)))
    +(3 4 6 12)
    +
    +
    + +

    +The problem of odd streams is that they do too much work, having an "off-by-one" +error that causes them to evaluate the next element of a stream before it is needed. +Mostly that's just a minor leak of space and time, but if evaluating the next element +causes an error, such as dividing by zero, it's a silly, unnecessary bug. +

    + +

    It is instructive to look at the coding differences between odd +and even streams. We expect the two constructors nil and +cons to be different, and they are; the odd +nil and cons return a strict list, but the +even nil and cons return promises. +Nil?, car and cdr change to +accomodate the underlying representation differences. +Cutoff is identical in the two versions, because it +doesn't return a stream. +

    + +

    The subtle but critical difference is in map and +countdown, the two functions that return streams. They +are identical except for the (delay (force ...)) that +wraps the return value in the even version. That looks odd, but is +correct. It is tempting to just eliminate the (delay (force +...)), but that doesn't work, because, given a promise +x, even though (delay (force x)) and +x both evaluate to x when forced, their semantics are +different, with x being evaluated and cached in one case but +not the other. That evaluation is, of course, the same "off-by-one" +error that caused the problem with odd streams. Note that +(force (delay x)) is something different entirely, +even though it looks much the same. +

    + +

    Unfortunately, that (delay (force ...)) is a major +notational inconvenience, because it means that the representation of +streams can't be hidden inside a few primitives but must infect each +function that returns a stream, making streams harder to use, harder +to explain, and more prone to error. Wadler et al solve the +notational inconvenience in their SML/NJ implementation by adding +special syntax -- the keyword lazy -- within the +compiler. Since Scheme allows syntax to be added via a macro, it +doesn't require any compiler modifications to provide streams. Shown +below is a Scheme implementation of Figure 1 to 3 from the paper, with +the (delay (force ...)) hidden within +stream-define, which is the syntax used to create a +function that returns a stream: +

    + + + + + + + +
    +
    ;;; FIGURE 1 -- ODD      
    +			 
    +(define nil1             
    +  '())                   
    +			 
    +(define (nil1? strm)     
    +  (null? strm))          
    +			 
    +(define-syntax cons1     
    +  (syntax-rules ()       
    +    ((cons1 obj strm)    
    +      (cons              
    +        obj              
    +          (delay         
    +            strm)))))    
    +			 
    +(define (car1 strm)      
    +  (car strm))            
    +			 
    +(define (cdr1 strm)      
    +  (force (cdr strm)))    
    +			 
    +                         
    +                         
    +                         
    +                         
    +                         
    +                         
    +                         
    +			 
    +(define (map1 func strm) 
    +                         
    +  (if (nil1? strm)       
    +    nil1                 
    +    (cons1               
    +      (func              
    +        (car1 strm))     
    +      (map1              
    +        func             
    +        (cdr1            
    +          strm)))))      
    +			 
    +(define (countdown1 n)   
    +                         
    +  (cons1                 
    +    n                    
    +    (countdown1          
    +      (- n 1))))         
    +			 
    +(define (cutoff1 n strm) 
    +  (cond                  
    +    ((zero? n) '())      
    +    ((nil1? strm) '())   
    +    (else                
    +      (cons              
    +        (car1 strm)      
    +        (cutoff1         
    +          (- n 1)        
    +          (cdr1          
    +            strm))))))   
    +
    +
    +
    ;;; FIGURE 2 -- EVEN     
    +			 
    +(define nil2             
    +  (delay '()))           
    +			 
    +(define (nil2? strm)     
    +  (null? (force strm))   
    +			 
    +(define-syntax cons2     
    +  (syntax-rules ()       
    +    ((cons2 obj strm)    
    +      (delay             
    +        (cons            
    +          obj            
    +          strm)))))      
    +			 
    +(define (car2 strm)      
    +  (car (force strm)))    
    +			 
    +(define (cdr2 strm)      
    +  (cdr (force strm)))    
    +			 
    +                         
    +                         
    +                         
    +                         
    +                         
    +                         
    +                         
    +			 
    +(define (map2 func strm) 
    +  (delay (force		 
    +    (if (nil2? strm)     
    +      nil2               
    +      (cons2             
    +        (func            
    +          (car2 strm))   
    +        (map2            
    +          func           
    +          (cdr2          
    +            strm)))))))  
    +			 
    +(define (countdown2 n)   
    +  (delay (force		 
    +    (cons2               
    +      n                  
    +      (countdown2        
    +        (- n 1))))))     
    +			 
    +(define (cutoff2 n strm) 
    +  (cond                  
    +    ((zero? n) '())      
    +    ((nil2? strm) '())   
    +    (else                
    +      (cons              
    +        (car2 strm)      
    +        (cutoff2         
    +          (- n 1)        
    +          (cdr2          
    +            strm))))))   
    +
    +
    +
    ;;; FIGURE 3 -- EASY
    +
    +(define nil3
    +  (delay '()))
    +
    +(define (nil3? strm)
    +  (null? (force strm)))
    +
    +(define-syntax cons3
    +  (syntax-rules ()
    +    ((cons3 obj strm)
    +      (delay
    +        (cons
    +          obj
    +          strm)))))
    +
    +(define (car3 strm)
    +  (car (force strm)))
    +
    +(define (cdr3 strm)
    +  (cdr (force strm)))
    +
    +(define-syntax stream-define
    + (syntax-rules ()
    +  ((stream-define (name args ...)
    +                  body0 body1 ...)
    +   (define (name args ...)
    +    (delay (force
    +     (begin body0 body1 ...)))))))
    +
    +(stream-define (map3 func strm)
    +
    +  (if (nil3? strm)
    +    nil3
    +    (cons3
    +      (func
    +        (car3 strm))
    +      (map3
    +        func
    +        (cdr3
    +          strm)))))
    +
    +(stream-define (countdown3 n)
    +
    +  (cons3
    +    n
    +    (countdown3
    +      (- n 1))))
    +
    +(define (cutoff3 n strm)
    +  (cond
    +    ((zero? n) '())
    +    ((nil3? strm) '())
    +    (else
    +      (cons
    +        (car3 strm)
    +        (cutoff3
    +          (- n 1)
    +          (cdr3
    +            strm))))))
    +
    +
    + +

    +It is now easy to see the notational inconvenience of Figure 2, as +the bodies of map1 and map3 are identical, +as are countdown1 and countdown3. All of +the inconvenience is hidden in the stream primitives, where it +belongs, so functions that use the primitives won't be burdened. This +means that users can just step up and use the library without any +knowledge of how the primitives are implemented, and indeed the +implementation of the primitives can change without affecting users of +the primitives, which would not have been possible with the streams of +Figure 2. With this implementation of streams, (cutoff3 4 (map3 12div +(countdown3 4))) evaluates to (3 4 6 12), as it should. +

    + +

    +This library provides streams that are even, not odd. This decision overturns years +of experience in the Scheme world, but follows the traditions of the "pure" functional +languages such as Miranda and Haskell. The primary benefit is elimination of the +"off-by-one" error that odd streams suffer. Of course, it is possible to use even +streams to represent odd streams, as Wadler et al show in their Figure 4, so nothing +is lost by choosing even streams as the default. +

    + +

    +Obviously, stream elements are evaluated when they are accessed, not when they are +created; that's the definition of lazy. Additionally, stream elements must be +evaluated only once, and the result cached in the event it is needed again; that's +common practice in all languages that support streams. Following the rule of R5RS +section 1.1 fourth paragraph, an implementation of streams is permitted to delete a +stream element from the cache and reclaim the storage it occupies if it can prove +that the stream element cannot possibly matter to any future computation. +

    + +

    +The fact that objects are permitted, but not required, to be reclaimed has a +significant impact on streams. Consider for instance the following example, due to +Joe Marshall. Stream-filter is a function that takes a predicate and a stream and +returns a new stream containing only those elements of the original stream that pass +the predicate; it can be simply defined as follows: +

    + +
        (stream-define (stream-filter pred? strm)
    +      (cond ((stream-null? strm) strm)
    +            ((pred? (stream-car strm))
    +              (stream-cons (stream-car strm)
    +                           (stream-filter pred? (stream-cdr strm))))
    +            (else (stream-filter pred? (stream-cdr strm)))))
    +
    + +

    +But this implementation of stream-filter has a problem: +

    + +
        (define (times3 n)
    +      (stream-car
    +        (stream-cdr
    +          (stream-cdr
    +            (stream-cdr
    +              (stream-cdr
    +                (stream-filter
    +                  (lambda (x) (zero? (modulo x n)))
    +                  from0)))))))
    +
    + +

    +Called as (times3 5), the function evaluates to 15, as +desired. But called as (times3 1000000), it churns the +disk, creating closures and caching each result as it counts slowly to +3,000,000; on most Scheme systems, this function will run out of +memory long before it computes an answer. A space leak occurs when +there is a gap between elements that pass the predicate, because the +naive definition hangs on to the head of the gap. Unfortunately, this +space leak can be very hard to fix, depending on the underlying Scheme +implementation, and solutions that work in one Scheme implementation +may not work in another. And, since R5RS itself doesn't specify any +safe-for-space requirements, this SRFI can't make any specific +requirements either. Thus, this SRFI encourages native +implementations of the streams described in this SRFI to "do the right +thing" with respect to space consumption, and implement streams that +are as safe-for-space as the rest of the implementation. Of course, +if the stream is bound in a scope outside the stream-filter +expression, there is nothing to be done except cache the elements as +they are filtered. +

    + +

    +Although stream-define has been discussed as the basic stream +abstraction, in fact it is the (delay (force ...)) +mechanism that is the basis for everything else. In the spirit of +Scheme minimality, the specification below gives stream-delay as the +syntax for converting an expression to a stream; stream-delay is +similar to delay, but returns a stream instead of a promise. Given +stream-delay, it is easy to create stream-lambda, which returns a +stream-valued function, and then stream-define, which binds a +stream-valued function to a name. However, stream-lambda and +stream-define are both library procedures, not fundamental to the use +of streams, and are thus excluded from this SRFI. +

    + +

    Specification

    + +

    A stream-pair is a data structure consisting of two fields called +the stream-car and stream-cdr. Stream-pairs are created +by the procedure stream-cons, and the stream-car and +stream-cdr fields are accessed by the procedures +stream-car and stream-cdr. There also +exists a special stream object called stream-null, which +is a single stream object with no elements, distinguishable from all +other stream objects and, indeed, from all other objects of any type. +The stream-cdr of a stream-pair must be either another stream-pair or +stream-null. +

    + +

    +Stream-null and stream-pair are used to represent streams. A stream +can be defined recursively as either stream-null or a stream-pair +whose stream-cdr is a stream. The objects in the stream-car fields of +successive stream-pairs of a stream are the elements of the stream. +For example, a two-element stream is a stream-pair whose stream-car is +the first element and whose stream-cdr is a stream-pair whose +stream-car is the second element and whose stream-cdr is stream-null. +A chain of stream-pairs ending with stream-null is finite and has a +length that is computed as the number of elements in the stream, which +is the same as the number of stream-pairs in the stream. A chain of +stream-pairs not ending with stream-null is infinite and has undefined +length. +

    + +

    +The way in which a stream can be infinite is that no element of the stream is +evaluated until it is accessed. Thus, any initial prefix of the stream can be +enumerated in finite time and space, but still the stream remains infinite. +Stream elements are evaluated only once; once evaluated, the value of a stream +element is saved so that the element will not be re-evaluated if it is accessed +a second time. Streams and stream elements are never mutated; all functions +involving streams are purely applicative. Errors are not required to be +signalled, as in R5RS section 1.3.2, although implementations are encouraged +to detect and report errors. +

    + +
    +
    +stream-null (constant)
    +
    +Stream-null is the distinguished nil stream, a single +Scheme object distinguishable from all other objects. If the last +stream-pair in a stream contains stream-null in its cdr field, the +stream is finite and has a computable length. However, there is no +need for streams to terminate. + +
    +    stream-null                                 => (stream)
    +
    +
    + +
    +(stream-cons object stream) (syntax)
    +
    +Stream-cons is the primitive constructor of streams, +returning a stream with the given object in its car field and the +given stream in its cdr field. The stream returned by +stream-cons must be different (in the sense of +eqv?) from every other Scheme object. The object may be +of any type, and there is no requirement that successive elements of a +stream be of the same type, although it is common for them to be. It +is an error if the second argument of stream-cons is not a stream. + +
        (stream-cons 'a stream-null)                => (stream 'a)
    +    (stream-cons 'a (stream 'b 'c 'd))          => (stream 'a 'b 'c 'd)
    +    (stream-cons "a" (stream 'b 'c))            => (stream "a" 'b 'c)
    +    (stream-cons 'a 3)                          => error
    +    (stream-cons (stream 'a 'b) (stream 'c))    => (stream (stream 'a 'b) 'c)
    +
    +
    + +
    +(stream? object) (function)
    +
    +Stream? returns #t if the object is a stream, and otherwise returns #f. A stream +object may be either the null stream or a stream pair created by stream-cons. + +
        (stream? stream-null)                       => #t
    +    (stream? (stream-cons 'a stream-null))      => #t
    +    (stream? 3)                                 => #f
    +
    +
    + +
    +(stream-null? object) (function)
    +
    +Stream-null? returns #t if the object is the distinguished nil stream, and +otherwise returns #f (stream-null? stream-null) => #t + (stream-null? (stream-cons 'a stream-null)) => #f + (stream-null? 3) => #f + +
    + +
    +(stream-pair? object) (function)
    +
    +Stream-pair? returns #t if the object is a stream pair created by stream-cons, and +otherwise returns #f. + +
        (stream-pair? stream-null)                  => #f
    +    (stream-pair? (stream-cons 'a stream-null)) => #t
    +    (stream-pair? 3)                            => #f
    +
    +
    + +
    +(stream-car stream) (function)
    +
    +Stream-car returns the object in the stream-car field of +a stream-pair. It is an error to attempt to evaluate the stream-car +of stream-null. + +
        (stream-car (stream 'a 'b 'c))              => a
    +    (stream-car stream-null)                    => error
    +    (stream-car 3)                              => error
    +
    +
    + +
    +(stream-cdr stream) (function)
    +
    +Stream-cdr returns the stream in the stream-cdr field of +a stream-pair. It is an error to attempt to evaluate the stream-cdr +of stream-null. + + +
        (stream-cdr (stream 'a 'b 'c))              => (stream 'b 'c)
    +    (stream-cdr stream-null)                    => error
    +    (stream-cdr 3)                              => error
    +
    +
    +
    + +
    +(stream-delay expression) (syntax)
    +
    +Stream-delay is the essential mechanism for operating on streams, taking an +expression and returning a delayed form of the expression that can be asked at +some future point to evaluate the expression and return the resulting value. The +action of stream-delay is analogous to the action of delay, but it is specific to +the stream data type, returning a stream instead of a promise; no corresponding +stream-force is required, because each of the stream functions performs the force +implicitly. + +
        (define from0
    +      (let loop ((x 0))
    +        (stream-delay
    +          (stream-cons x (loop (+ x 1))))))
    +    from0                                       => (stream 0 1 2 3 4 5 6 ...)
    +
    +
    + +
    (stream object ...) (library function) +
    +Stream returns a newly allocated finite stream of its arguments, in order. + +
        (stream 'a (+ 3 4) 'c)                      => (stream 'a 7 'c)
    +    (stream)                                    => stream-null
    +
    +
    + +
    +(stream-unfoldn generator seed n) +(function)
    +
    +Stream-unfoldn returns n streams whose contents are produced by successive calls +to generator, which takes the current seed as an arguments and returns n + 1 +values: + +

    +(proc seed) -> seed result0 ... resultN + +

    +where resultI indicates how to produce the next element of the Ith result stream: +

    + + + + + + + + + + + + + + +
    (value)value is the next car of this result stream
    #fno new information for this result stream +
    +()the end of this result stream has been reached +
    + +Note that getting the next element in any particular result stream may require +multiple calls to generator. + +
        (define (take5 s)
    +      (stream-unfoldn
    +        (lambda (x)
    +          (let ((n (car x)) (s (cdr x)))
    +            (if (zero? n)
    +                (values 'dummy '())
    +                (values
    +                  (cons (- n 1) (stream-cdr s))
    +                  (list (stream-car s))))))
    +        (cons 5 s)
    +        1))
    +    (take5 from0)                              => (stream 0 1 2 3 4)
    +
    +
    + +
    +(stream-map function stream ...) (library +function)
    +
    +Stream-map creates a newly allocated stream built by +applying function elementwise to the elements of the streams. The +function must take as many arguments as there are streams and return a +single value (not multiple values). The stream returned by stream-map +is finite if the given stream is finite, and infinite if the given +stream is infinite. If more than one stream is given, stream-map +terminates when any of them terminate, or is infinite if all the +streams are infinite. The stream elements are evaluated in order. + +
        (stream-map (lambda (x) (+ x x)) from0)      => (stream 0 2 4 6 8 10 ...)
    +    (stream-map + (stream 1 2 3) (stream 4 5 6)) => (stream 5 7 9)
    +    (stream-map (lambda (x) (expt x x))
    +      (stream 1 2 3 4 5))                        => (stream 1 4 27 256 3125)
    +
    +
    + +
    +(stream-for-each procedure stream ...) +(library function)
    +
    +Stream-for-each applies procedure elementwise to the elements of the streams, +calling the procedure for its side effects rather than for its values. The +procedure must take as many arguments as there are streams. The value returned by +stream-for-each is unspecified. The stream elements are visited in order. + +
        (stream-for-each display from0)             => no value, prints 01234 ...
    +
    +
    + +
    +(stream-filter predicate? stream) (library function)
    +
    +Stream-filter applies predicate? to each element +of stream and creates a newly allocated stream consisting of those +elements of the given stream for which predicate? returns a +non-#f value. Elements of the output stream are in the +same order as they were in the input stream, and are tested by +predicate? in order. + +
        (stream-filter odd? stream-null)            => stream-null
    +    (take5 (stream-filter odd? from0))          => (stream 1 3 5 7 9)
    +
    +
    +
    + +

    Implementation

    + +

    A reference implementation of streams is shown below. It strongly +prefers simplicity and clarity to efficiency, and though a reasonable +attempt is made to be safe-for-space, no promises are made. The reference +implementation relies on the mechanism for defining record types of SRFI-9, and the functions +any and every from SRFI-1. The +stream-error function aborts by calling error as +defined in SRFI 23. + +

    ;;; PROMISES A LA SRFI-45:
    +
    +;;; A separate implementation is necessary to
    +;;; have promises that answer #t to stream?
    +;;; This requires lots of complicated type conversions.
    +
    +(define-record-type s:promise (make-s:promise kind content) s:promise?
    +  (kind    s:promise-kind    set-s:promise-kind!)
    +  (content s:promise-content set-s:promise-content!))
    +
    +(define-record-type box (make-box x) box?
    +  (x unbox set-box!))
    +
    +(define-syntax srfi-40:lazy
    +  (syntax-rules ()
    +    ((lazy exp)
    +     (make-box (make-s:promise 'lazy (lambda () exp))))))
    +
    +(define (srfi-40:eager x)
    +  (make-stream (make-box (make-s:promise 'eager x))))
    +
    +(define-syntax srfi-40:delay
    +  (syntax-rules ()
    +    ((srfi-40:delay exp) (srfi-40:lazy (srfi-40:eager exp)))))
    +
    +(define (srfi-40:force promise)
    +  (let ((content (unbox promise)))
    +    (case (s:promise-kind content)
    +      ((eager) (s:promise-content content))
    +      ((lazy)
    +       (let* ((promise* (stream-promise ((s:promise-content content))))
    +              (content  (unbox promise)))
    +         (if (not (eqv? 'eager (s:promise-kind content)))
    +             (begin
    +               (set-s:promise-kind! content (s:promise-kind (unbox promise*)))
    +               (set-s:promise-content! content (s:promise-content (unbox promise*)))
    +               (set-box! promise* content)))
    +         (srfi-40:force promise))))))
    +
    +
    +;;; STREAM -- LIBRARY OF SYNTAX AND FUNCTIONS TO MANIPULATE STREAMS
    +
    +;;; A stream is a new data type, disjoint from all other data types, that
    +;;; contains a promise that, when forced, is either nil (a single object
    +;;; distinguishable from all other objects) or consists of an object
    +;;; (the stream element) followed by a stream.  Each stream element is
    +;;; evaluated exactly once, when it is first retrieved (not when it is
    +;;; created); once evaluated its value is saved to be returned by
    +;;; subsequent retrievals without being evaluated again.
    +
    +;; STREAM-TYPE -- type of streams
    +;; STREAM? object -- #t if object is a stream, #f otherwise
    +(define-record-type stream-type
    +  (make-stream promise)
    +  stream?
    +  (promise stream-promise))
    +
    +;;; UTILITY FUNCTIONS
    +
    +;; STREAM-ERROR message -- print message then abort execution
    +;  replace this with a call to the native error handler
    +;  if stream-error returns, so will the stream library function that called it
    +(define stream-error error)
    +
    +;;; STREAM SYNTAX AND FUNCTIONS
    +
    +;; STREAM-NULL -- the distinguished nil stream
    +(define stream-null (make-stream (srfi-40:delay '())))
    +
    +;; STREAM-CONS object stream -- primitive constructor of streams
    +(define-syntax stream-cons
    +  (syntax-rules ()
    +    ((stream-cons obj strm)
    +     (make-stream
    +      (srfi-40:delay
    +       (if (not (stream? strm))
    +           (stream-error "attempt to stream-cons onto non-stream")
    +           (cons obj strm)))))))
    +
    +;; STREAM-NULL? object -- #t if object is the null stream, #f otherwise
    +(define (stream-null? obj)
    +  (and (stream? obj) (null? (srfi-40:force (stream-promise obj)))))
    +
    +;; STREAM-PAIR? object -- #t if object is a non-null stream, #f otherwise
    +(define (stream-pair? obj)
    +  (and (stream? obj) (not (null? (srfi-40:force (stream-promise obj))))))
    +
    +;; STREAM-CAR stream -- first element of stream
    +(define (stream-car strm)
    +  (cond ((not (stream? strm)) (stream-error "attempt to take stream-car of non-stream"))
    +        ((stream-null? strm)  (stream-error "attempt to take stream-car of null stream"))
    +        (else (car (srfi-40:force (stream-promise strm))))))
    +
    +;; STREAM-CDR stream -- remaining elements of stream after first
    +(define (stream-cdr strm)
    +  (cond ((not (stream? strm)) (stream-error "attempt to take stream-cdr of non-stream"))
    +        ((stream-null? strm)  (stream-error "attempt to take stream-cdr of null stream"))
    +        (else (cdr (srfi-40:force (stream-promise strm))))))
    +
    +;; STREAM-DELAY object -- the essential stream mechanism
    +(define-syntax stream-delay
    +  (syntax-rules ()
    +    ((stream-delay expr)
    +      (make-stream
    +        (srfi-40:lazy expr)))))
    +
    +;; STREAM object ... -- new stream whose elements are object ...
    +(define (stream . objs)
    +  (let loop ((objs objs))
    +    (stream-delay
    +      (if (null? objs)
    +          stream-null
    +          (stream-cons (car objs) (loop (cdr objs)))))))
    +
    +;; STREAM-UNFOLDN generator seed n -- n+1 streams from (generator seed)
    +(define (stream-unfoldn gen seed n)
    +  (define (unfold-result-stream gen seed)
    +    (let loop ((seed seed))
    +      (stream-delay
    +        (call-with-values
    +          (lambda () (gen seed))
    +          (lambda (next . results)
    +            (stream-cons results (loop next)))))))
    +  (define (result-stream->output-stream result-stream i)
    +    (stream-delay
    +      (let ((result (list-ref (stream-car result-stream) i)))
    +        (cond ((pair? result)
    +                (stream-cons (car result)
    +                             (result-stream->output-stream
    +                               (stream-cdr result-stream) i)))
    +              ((not result)
    +                (result-stream->output-stream (stream-cdr result-stream) i))
    +              ((null? result) stream-null)
    +              (else (stream-error "can't happen"))))))
    +  (define (result-stream->output-streams result-stream n)
    +    (let loop ((i 0) (outputs '()))
    +      (if (= i n)
    +        (apply values (reverse outputs))
    +        (loop (+ i 1)
    +              (cons (result-stream->output-stream result-stream i)
    +                    outputs)))))
    +  (result-stream->output-streams (unfold-result-stream gen seed) n))
    +
    +;; STREAM-MAP func stream ... -- stream produced by applying func element-wise
    +(define (stream-map func . strms)
    +  (cond ((not (procedure? func)) (stream-error "non-functional argument to stream-map"))
    +        ((null? strms) (stream-error "no stream arguments to stream-map"))
    +        ((not (every stream? strms)) (stream-error "non-stream argument to stream-map"))
    +        (else (let loop ((strms strms))
    +                (stream-delay
    +                  (if (any stream-null? strms)
    +                      stream-null
    +                      (stream-cons (apply func (map stream-car strms))
    +                                   (loop (map stream-cdr strms)))))))))
    +
    +;; STREAM-FOR-EACH proc stream ... -- apply proc element-wise for side-effects
    +(define (stream-for-each proc . strms)
    +  (cond ((not (procedure? proc)) (stream-error "non-functional argument to stream-for-each"))
    +        ((null? strms) (stream-error "no stream arguments to stream-for-each"))
    +        ((not (every stream? strms)) (stream-error "non-stream argument to stream-for-each"))
    +        (else (let loop ((strms strms))
    +                (if (not (any stream-null? strms))
    +                    (begin (apply proc (map stream-car strms))
    +                           (loop (map stream-cdr strms))))))))
    +
    +;; STREAM-FILTER pred? stream -- new stream including only items passing pred?
    +(define (stream-filter pred? strm)
    +  (cond ((not (procedure? pred?)) (stream-error "non-functional argument to stream-filter"))
    +        ((not (stream? strm)) (stream-error "attempt to apply stream-filter to non-stream"))
    +        (else (stream-unfoldn
    +                (lambda (s)
    +                  (values
    +                    (stream-cdr s)
    +                    (cond ((stream-null? s) '())
    +                          ((pred? (stream-car s)) (list (stream-car s)))
    +                          (else #f))))
    +                strm
    +                1))))
    + +

    References

    + +
      +
    • + +Harold Abelson, Gerald Jay Sussman, Julie Sussman: +Structure and + Interpretation of Computer Programs, 1996, MIT Press. +
    • +
    • + +Lawrence C. Paulson: ML for the Working Programmer, +2nd edition, Cambridge University Press, 1996. +
    • +
    • + +George Springer and Daniel P. Friedman: +Scheme and the Art of Programming, MIT Press and McGraw-Hill, 1989. +
    • +
    • + +Philip Wadler, Walid Taha, and David MacQueen: +"How to +add laziness to a strict language without even being odd", 1998 ACM SIGPLAN +Workshop on ML, pp. 24-30. +(available +here in various formats) +
    • +
    • + +Patrick H. Winston, Berthold K. Horn: Lisp, 3rd edition, +Addison Wesley, 1989. +
    +

    Copyright

    +

    Copyright (C) 2003 by Philip L. Bewig of Saint Louis, Missouri, United States of +America. All rights reserved.

    + +

    +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: +

    +

    +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. +

    +

    +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +

    + +
    +
    Editor: Mike Sperber
    + + +Last modified: Sat Sep 11 12:40:31 MST 2004 + + + diff --git a/doc/srfi-std/srfi-42.html b/doc/srfi-std/srfi-42.html new file mode 100644 index 0000000000..a3cb664590 --- /dev/null +++ b/doc/srfi-std/srfi-42.html @@ -0,0 +1,2132 @@ + + + + SRFI 42: Eager Comprehensions + + + + +

    Title

    + +SRFI 42: Eager Comprehensions + +

    Author

    + +Sebastian Egner + +

    Status

    + +

    This SRFI is currently in ``final'' status. To see an explanation of +each status that a SRFI can hold, see here. +To comments +this SRFI, please mail to + +srfi-42@srfi.schemers.org. +See +instructions here to subscribe to the list. You can access +the discussion via + +the archive of the mailing list. +You can access +post-finalization messages via + +the archive of the mailing list. +

    +
      +
    • Received: 2003/02/20 +
    • Draft: 2003/02/20-2003/04/20 +
    • Revised: 2003/07/07 +
    • Final: 2003/07/07 +
    + +

    Abstract

    + +This SRFI defines a modular and portable mechanism for +eager comprehensions extending the algorithmic language Scheme +[R5RS]. +An eager comprehension is a convenient notation +for one or more nested or parallel loops generating +a sequence of values, and accumulating this sequence into a result. +In its most simple form, a comprehension according to this SRFI +looks like this: + +
    (list-ec (: i 5) (* i i)) => (0 1 4 9 16).
    + +Here, i is a local variable that is sequentially +bound to the values 0, 1, ..., 4, and the squares of these +numbers are collected in a list. +The following example illustrates most conventions of this SRFI +with respect to nesting and syntax: + +
    (list-ec (: n 1 4) (: i n) (list n i)) => ((1 0) (2 0) (2 1) (3 0) (3 1) (3 2)).
    + +In the example, the variable n is first bound to 1 +then to 2 and finally to 3, and for each binding of n +the variable i is bound to the values +0, 1, ..., n-1 in turn. +The expression (list n i) constructs a two-element list for +each bindings, and the comprehension list-ec collects +all these results in a list.

    + +The mechanism defined in this SRFI has the following properties: + +

      + +
    • +The set of comprehensions defined for this SRFI is inspired by +those procedures and macros of [R5RS, 6.] +leading to natural comprehensions such as +list-ec, +append-ec, +sum-ec, +min-ec, +every?-ec, +do-ec, and others. +Some other natural comprehensions (e.g. gcd-ec) have +not been included into this SRFI due to their low significance +for most applications. +On the other hand, a few comprehensions +(fold-ec, +fold3-ec) +not inspired by [R5RS] have been +included due to their broad applicability. +
    • + +
    • +There are typed generators +(:list, :string, ...) +expecting certain types of objects for their arguments. +These generators usually produce code as efficient as +hand coded do-loops. +
    • + +
    • +There is also the special generator : +(read 'run through') +dispatching on the value of its argument list at runtime. +In the examples above, one or two integers were used to define a range. +The convenience of omitting the type comes at a certain +performance penalty, both per iteration and during startup of the loop. +
    • + +
    • +Generators can be nested depth-first (as in the example above), +run in parallel (with an optional index variable +or more generally with :parallel), +and can be stopped early before (:while) +or after (:until) +producing the current value. +
    • + +
    • +The sequence of values can be filtered +(if, not, +and, or), +intermediate commands can be evaluated between +generators (begin), and +intermediate variables can be introduced (:let). +
    • +
    • +The mechanism is fully modular. +This means that no existing macro or procedure needs +to be modified when adding +application-specific comprehensions, +application-specific typed generators, +or application-specific dispatching generators. +
    • + +
    • +Syntactically, this SRFI uses the +[outer .. inner | expr] convention, +meaning that the most right generator (inner) spins fastest +and is followed by the result expression over which the +comprehension ranges (expr). +Refer to the Section "Design Rationale". +Moreover, the syntax is strictly prefix and the naming +convention my-comprehension-ec, +:my-generator is used systematically. +
    • + +
    + +

    + +The remainder of this document is organized as follows. +In section Rationale a brief +introduction is given what the motivation is for this SRFI. +The following section Specification +defines the mechanism. +Section Suggestions for +Application-specific Extensions presents some ideas +how extensions using this SRFI could look like. +The section Design Rationale contains +some considerations that went into the design of the +mechanism as defined in the specification. +The following section Related Work +and Acknowledgements briefly reviews other proposals +related to Scheme and comprehensions or loops. +Finally, the section Reference +Implementation gives source code for a reference +implementation together with a collection of runnable +examples and a few examples on extensions. + + +

    Rationale

    + +The purpose of this SRFI is to provide a compact notation +for many common cases of loops, in particular those +constructing values for further computation. +The origin of this SRFI is my frustration that +there is no simple for the list of integers +from 0 to n-1. +With this SRFI it is (list-ec (: i n) i). +Refer to the collection of examples for the +reference implementation +what it can be used for, and what it should not be used for. +To give a practically useful example, +the following procedure computes the sorted +list of all prime numbers below a certain bound +(you may want to run it yourself to get an idea of +its efficiency): + +
    
    +(define (eratosthenes n)          ; primes in {2..n-1} for n >= 1
    +  (let ((p? (make-string n #\1)))
    +    (do-ec (:range k 2 n)
    +           (if (char=? (string-ref p? k) #\1))
    +           (:range i (* 2 k) n k)
    +           (string-set! p? i #\0) )
    +    (list-ec (:range k 2 n) (if (char=? (string-ref p? k) #\1)) k) ))
    + + +Apart from make simple things simple, +there is no other paradigm involved for this SRFI. +In particular, it is not the ambition to implement the +powerful lazy list comprehensions of other functional +programming languages in Scheme. +If you are looking for that you may want to refer to +[SRFI 40]. +(The usual definition of the stream of all primes does +in fact also use Eratosthenes' sieve method. +It is instructive to compare.)

    + +The main focus of the design of this SRFI is portability under +[R5RS] and modularity for extension. +Portability is achieved by limiting the features included. +Modularity for generators is achieved by a special +implementation technique using Continuation +Passing Style for macros (which I learned from +Richard Kelsey's implementation of "Macros for writing loops", +[MWL]) and by limiting the +expressive power of generators. +Modularity for comprehensions requires no additional effort. +As modularity was a major design goal, I hope many people will +find it easy to define their own comprehensions and generators. +As a starting point for doing so, I have included several +suggestions for extensions.

    + +For a detailed motivation of the design decisions, +please refer to the Section "Design Rationale".

    + +

    Specification

    + +A comprehensions is a hygienic referentially transparent macro +in the sense of [R5RS, 4.3.]. +The macros extend the <expression>-syntax +defined in [R5RS, 7.1.3.]. +The main syntactic pattern used for defining a comprehension is +<qualifier>, representing a generator or a filter. +It is defined in Section "Qualifiers".

    + +The most important instances of <qualifier> are generators. +These are defined in Section "Generators". +Generators come in three flavors, +as typed generators +(:list, +:range etc.), +as the dispatching generator : +(pronounced as 'run through'), +and as combined and modified generators +(:parallel, +:while, +:until). +Most generators in this SRFI also support an optional +index variable counting the +values being generated.

    + +Finally, it is explained how to add a new +application-specific comprehension, +how to add a new application-specific typed generator, +and how to add a new +application-specific dispatching generator. +As this concerns code unknown at the time this is being written, +the explanation should not be taken as +a specification in the literal sense. +It rather suggests a convention to follow in order to +ensure new comprehensions and generators blend seamlessly with +the ones defined in this SRFI.

    + + +

    Comprehensions

    + +
    +
    +(do-ec <qualifier>* <command>) +
    + +
    +Evaluates the <command> exactly once +for each binding in the sequence defined by the qualifiers. +If there are no qualifiers <command> +is evaluated exactly once. +The expression is evaluated for its side-effects only. +The result of the comprehension is unspecified. +
    +
    + +
    +
    +(list-ec <qualifier>* <expression>) +
    + +
    +The list of values obtained by evaluating +<expression> once for each binding in the sequence +defined by the qualifiers. +If there are no qualifiers the result is the list with +the value of <expression>. +
    +
    + +
    +
    +(append-ec <qualifier>* <expression>) +
    + +
    +The list obtained by appending all values of <expression>, +which must all be lists.
    +Think of it as +(apply append (list-ec <qualifier>* <expression>)). +
    +
    + +
    +
    +(string-ec <qualifier>* <expression>) +
    + +
    +The string of all values of <expression>.
    +Think of it as +(list->string (list-ec <qualifier>* <expression>)). +
    +
    + +
    +
    +(string-append-ec <qualifier>* <expression>) +
    + +
    +The string obtained by appending all values of <expression>, +which must all be strings.
    +Think of it as +(apply string-append (list-ec <qualifier>* <expression>)). +
    +
    + +
    +
    +(vector-ec <qualifier>* <expression>) +
    + +
    +The vector of all values of <expression>.
    +Think of it as +(list->vector (list-ec <qualifier>* <expression>)). +
    +
    + +
    +
    +(vector-of-length-ec <k> <qualifier>* <expression>) +
    + +
    +The vector of all values of <expression>, +of which there must be exactly <k>. +This comprehension behaves like vector-ec +but can be implemented more efficiently. +
    +
    + +
    +
    +(sum-ec <qualifier>* <expression>) +
    + +
    +The sum of all values of <expression>.
    +Think of it as +(apply + (list-ec <qualifier>* <expression>)). +
    +
    + +
    +
    +(product-ec <qualifier>* <expression>) +
    + +
    +The product of all values of <expression>.
    +Think of it as +(apply * (list-ec <qualifier>* <expression>)). +
    +
    + +
    +
    +(min-ec <qualifier>* <expression>)
    +(max-ec <qualifier>* <expression>) +
    + +
    +The minimum and maximum of all values of <expression>. +The sequence of values must be non-empty. +Think of these as
    +(apply min (list-ec <qualifier>* <expression>))
    +(apply max (list-ec <qualifier>* <expression>)).

    + +If you want to return a default value in case the sequence is empty +you may want to consider
    +(fold3-ec 'infinity <qualifier>* <expression> min min). +

    +
    + +
    +
    +(any?-ec <qualifier>* <test>) +
    + +
    +Tests whether any value of <test> in the sequence +of bindings specified by the qualifiers is non-#f. +If this is the case, #t is returned, otherwise #f. +If there are no bindings in the sequence specified by the qualifiers +at all then the result is #f. +The enumeration of values stops after the first non-#f +encountered. +
    +
    + +
    +
    +(every?-ec <qualifier>* <test>) +
    + +
    +Tests whether all values of <test> are non-#f. +If this is the case, #t is returned, otherwise #f. +If the sequence is empty the result is #t. +Enumeration stops after the first #f. +
    +
    + +
    +
    +(first-ec <default> <qualifier>* <expression>)
    +(last-ec  <default> <qualifier>
    * <expression>) +
    + +
    +The first or last value of <expression> +in the sequence of bindings specified by the qualifiers. +Before enumeration, the result is initialized +with the value of <default>; +so this will be the result if the sequence is empty. +Enumeration is terminated in first-ec +when the first value has been computed. +
    +
    + +
    +
    +(fold-ec  <x0> <qualifier>* <expression> <f2>)
    +(fold3-ec <x0> <qualifier>
    * <expression> <f1> <f2>) +
    + +
    +Reduces the sequence x[0], x[1], ..., x[n-1] +of values obtained by evaluating <expression> once +for each binding as specified by <qualifier>*. +The arguments <x0>, <f2>, +and <f1>, all syntactically equivalent to +<expression>, specify the reduction process.

    + +The reduction process for fold-ec is defined as follows. +A reduction variable x is initialized to the value +of <x0>, +and for each k in {0, ..., n-1} the command +(set! x (<f2> x[k] x)) +is evaluated. +Finally, x is returned as the value of the comprehension.

    + +The reduction process for fold3-ec is different. +If and only if n = 0, i.e. the sequence is empty, then +<x0> is evaluated and returned as the value +of the comprehension. +Otherwise, a reduction variable x is initialized +to the value of (<f1> x[0]), +and for each k in {1, ..., n-1} the command +(set! x (<f2> x[k] x)) +is evaluated. +Finally, x is returned as the value of the comprehension.

    + +As the order of the arguments suggests, +<x0> is evaluated outside the scope of the +qualifiers, whereas the reduction expressions involving +<f1> and <f2> are +inside the scope of the qualifiers (so they may depend on +any variable introduced by the qualifiers). +Note that <f2> is evaluated repeatedly, +with any side-effect or overhead this might have.

    + +The main purpose of these comprehensions is +implementing other comprehensions as special cases. +They are generalizations of the procedures +fold and reduce in the sense of +[SRFI 1]. +(Concerning naming and argument order, please refer to +the discussion archive of SRFI 1, in particular +the posting [Folds].) +Note that fold3-ec is defined such that +<x0> is only evaluated in case the +sequence is empty. +This allows raising an error for the empty sequence, +as in the example definition of min-ec below. +

    +
    + +
    +
    +<application-specific comprehension> +
    + +
    +An important aspect of this SRFI is a modular mechanism +to define application-specific comprehensions. +To create a new comprehension a hygienic macro +of this name is defined. +The macro transforms the new comprehension patterns +into instances of do-ec, which is the most +fundamental eager comprehension, or any other comprehension +already defined. +For example, the following code defines +list-ec and +min-ec +in terms of fold-ec +and fold3-ec: + +
    
    +(define-syntax list-ec
    +  (syntax-rules ()
    +    ((list-ec etc1 etc ...)
    +     (reverse (fold-ec '() etc1 etc ... cons)) )))
    +
    +(define-syntax min-ec
    +  (syntax-rules ()
    +    ((min-ec etc1 etc ...)
    +     (fold3-ec (min) etc1 etc ... min min) )))
    + +Note that the pattern etc1 etc ... matches +the syntax <qualifier>* <expression> +without separate access to <qualifier>* +and <expression>. +In order to define a comprehension that does need explicit +access to the <expression>-part, +the following method is used. +First, all qualifiers are collected into a +nested-qualifier, and then the +'exactly one qualifier'-case is implemented. +For illustration, the following code defines +fold3-ec in terms of +do-ec: + + +
    
    +(define-syntax fold3-ec
    +  (syntax-rules (nested)
    +    ((fold3-ec x0 (nested q1 ...) q etc1 etc2 etc3 etc ...)
    +     (fold3-ec x0 (nested q1 ... q) etc1 etc2 etc3 etc ...) )
    +    ((fold3-ec x0 q1 q2 etc1 etc2 etc3 etc ...)
    +     (fold3-ec x0 (nested q1 q2) etc1 etc2 etc3 etc ...) )
    +    ((fold3-ec x0 expression f1 f2)
    +     (fold3-ec x0 (nested) expression f1 f2) )
    +
    +    ((fold3-ec x0 qualifier expression f1 f2)
    +     (let ((result #f) (empty #t))
    +       (do-ec qualifier
    +              (let ((value expression)) ; don't duplicate code
    +                (if empty
    +                    (begin (set! result (f1 value))
    +                           (set! empty #f) )
    +                    (set! result (f2 value result)) )))
    +       (if empty x0 result) ))))
    + +Finally, observe that the newly defined fold3-ec +comprehension inherits all types of qualifiers supported by +do-ec, including all application-specific generators; +no further definitions are necessary. +
    +
    + + +

    Qualifiers

    + +This section defines the syntax <qualifier>. +The nesting of qualifiers is from left (outer) to right (inner). +In other words, the rightmost generator spins fastest. +The nesting also defines the scope of the variables introduced +by the generators. +This implies that inner generators may depend on the variables +of outer generators. +The sequence of enumeration of values is strictly depth first. +These conventions are illustrated by the +first example.

    + +The syntax <qualifier> consists of the following alternatives:

    + + +

    +
    +<generator> +
    + +
    +Enumerates a sequence of bindings of one or more variables. +The scope of the variables starts at the generator and extends +over all subsequent qualifiers and expressions in the comprehension. +The <generator>-syntax is defined in +Section "Generators". +
    +
    + +
    +
    +(if <test>) +
    + +
    +Filters the sequence of bindings by testing if +<test> evaluates to non-#f. +Only for those bindings for which this is the case, +the subsequent qualifiers of the comprehension are evaluated. +
    +
    + +
    +
    +(not <test>)
    +(and <test>
    *)
    +(or  <test>
    *) +
    + +
    +Abbreviated notations for filters of the form +(if (not <test>)), +(if (and <test>*)), and +(if (or <test>*)). +These represent frequent cases of filters. +
    +
    + +
    +
    +(begin <sequence>) +
    + +
    +Evaluate <sequence>, consisting of +<command>* <expression>, +once for each binding of the variables defined by the +previous qualifiers in the comprehension. +Using this qualifier, side effects can be inserted +into the body of a comprehension. +
    +
    + +
    +
    +(nested <qualifier>*) +
    + +
    +A syntactic construct to group qualifiers. +The meaning of a qualifier according to the +nested-syntax is the same as +inserting <qualifier>* +into the enclosing comprehension. +This construct can be used to reduce comprehensions with several +qualifiers into a form with exactly one qualifier. +
    +
    + + +

    Generators

    + +This section defines the syntax <generator>. +Each generator defines a sequence of bindings through +which one or more variables are run. +The scope of the variables begins after the closing +parenthesis of the generator expression and extends +to the end of the comprehension it is part of.

    + +The variables defined by the generators +are specified using the syntax

    + +

    +<vars> --> <variable1> [ (index <variable2>) ],
    + +where <variable1> runs through the values in +the sequence defined by the generator, and the optional +<variable2> is an exact integer-valued +index variable counting the values (starting from 0). +The names of the variables must be distinct. +The following example illustrates the index variable:

    + +

    
    +(list-ec (: x (index i) "abc") (list x i)) => ((#\a 0) (#\b 1) (#\c 2))
    + +Unless defined otherwise, all generators make sure that the +expressions provided for their syntactic arguments are +evaluated exactly once, before enumeration begins. +Moreover, it may be assumed that the generators do not +copy the code provided for their arguments, because that +could lead to exponential growth in code size. +Finally, it is possible to assign a value to the variables +defined by a generator, but the effect of this assignment +is unspecified.

    + +The syntax <generator> consists of the following alternatives:

    + +

    +
    +(: <vars> <arg1> <arg>*) +
    + +
    +First the expressions <arg1> <arg>* +are evaluated into a[1] a[2] ... a[n] +and then a global dispatch procedure is used to dispatch on +the number and types of the arguments and run the resulting +generator. + + +Initially (after loading the SRFI), +the following cases are recognized:

    + + + + + + + + + + + + + + + + + + + + + + + + +
    :list +if +for all i in {1..n}: +(list? a[i]). +
    :string +if +for all i in {1..n}: +(string? a[i]). +
    :vector +if +for all i in {1..n}: +(vector? a[i]). +
    :range +if +n in {1..3} and +for all i in {1..n}: +(integer? a[i]) and +(exact? a[i]). +
    :real-range +if +n in {1..3} and +for all i in {1..n}: +(real? a[i]). +
    :char-range +if +n = 2 and +for all i in {1, 2}: +(char? a[i]). +
    :port +if +n in {1,2} and +(input-port? a[1]) and +(procedure? a[2]). +

    + + +The current dispatcher can be retrieved as +(:-dispatch-ref), a new dispatcher d +can be installed by +(:-dispatch-set! d) +yielding an unspecified result, +and a copy of the initial dispatcher can be obtained as +(make-initial-:-dispatch). +Please refer to the section below +for recommendation how to add cases to the dispatcher. +

    +
    + +
    +
    +(:list   <vars> <arg1> <arg>*)
    +(:string <vars> <arg1> <arg>
    *)
    +(:vector <vars> <arg1> <arg>
    *) +
    + +
    +Run through one or more lists, strings, or vectors. +First all expressions in <arg1> <arg>* +are evaluated and then all elements of the resulting values +are enumerated from left to right. +One can think of it as first appending all arguments and +then enumerating the combined object. +As a clarifying example, consider
    +(list-ec (:string c (index i) "a" "b") (cons c i)) => +((#\a . 0) (#\b . 1)). +
    +
    + +
    +
    +(:integers <vars>) +
    + +
    +Runs through the sequence 0, 1, 2, ... of non-negative integers. +This is most useful in combination with +:parallel, +:while, and +:until or with +a non-local exit in the body of the comprehension. +
    +
    + +
    +
    +(:range <vars>         <stop>)
    +(:range <vars> <start> <stop>)
    +(:range <vars> <start> <stop> <step>)
    +
    + +
    +Runs through a range of exact rational numbers.

    + +The form (:range <vars> <stop>) +evaluates the expression <stop>, +which must result in an exact integer n, +and runs through the finite sequence 0, 1, 2, ..., n-1. +If n is zero or negative the sequence is empty.

    + +The form (:range <vars> <start> <stop>) +evaluates the expressions <start> and +<stop>, +which must result in exact integers a and b, +and runs through the finite sequence +a, a+1, a+2, ..., b-1. +If b is less or equal a then the sequence is empty.

    + +The form (:range <vars> <start> <stop> <step>) +first evaluates the expressions <start>, +<stop>, and <step>, +which must result in exact integers a, b, and s +such that s is unequal to zero. +Then the sequence +a, a + s, a + 2 s, ..., a + (n-1) s +is enumerated where n = ceil((b-a)/s). +In other words, the sequence starts at a, increments by s, +and stops when the next value would reach or cross b. +If n is zero or negative the sequence is empty. +

    +
    + +
    +
    +(:real-range <vars>         <stop>)
    +(:real-range <vars> <start> <stop>)
    +(:real-range <vars> <start> <stop> <step>)
    +
    + +
    +Runs through a range of real numbers using an explicit index variable. +This form of range enumeration avoids accumulation of rounding errors +and is the one to use if any of the numbers defining the range is +inexact, not an integer, or a bignum of large magnitude.

    + +Providing default value 0 for <start> and +1 for <step>, the generator first +evaluates <start>, <stop>, +and <step>, which must result in reals +a, b, and s such that +n = (b-a)/s is also representable +as a real. +Then the sequence 0, 1, 2, ... is enumerated while the +current value i is less than n, and the +variable in <vars> is bound to the +value a + i s. +If any of the values a, b, or s is +non-exact then all values in the sequence are non-exact. +

    +
    + +
    +
    +(:char-range <vars> <min> <max>) +
    + +
    +Runs through a range of characters. +First <min> and <max> are +evaluated, which must result in two characters a and b. +Then the sequence of characters +a, a+1, a+2, ..., b +is enumerated in the order defined by char<=? +in the sense of [R5RS, 6.3.4.]. +If b is smaller than a then the sequence is empty. +(Note that b is included in the sequence.) +
    +
    + +
    +
    +(:port <vars> <port>)
    +(:port <vars> <port> <read-proc>) +
    + +
    +Reads from the port until the eof-object is read. +Providing the default read for +<read-proc>, the generator first +evaluates <port> and +<read-proc>, which must result +in an input port p and a procedure r. +Then the variable is run through the sequence obtained +by (r p) +while the result does not satisfy eof-object?. +
    +
    + +
    +
    +(:dispatched <vars> <dispatch> <arg1> <arg>*) +
    + +
    +Runs the variables through a sequence defined by <dispatch> +and <arg1> <arg>*. +The purpose of :dispatched is implementing +dispatched generators, in particular the predefined dispatching +generator :.

    + +The working of :dispatched is as follows. +First <dispatch> and +<arg1> <arg>* are evaluated, +resulting in a procedure d (the 'dispatcher') and +the values a[1] a[2] ... a[n]. +Then +(d (list a[1] a[2] ... a[n] )) +is evaluated, resulting in a value g. +If g is not a procedure then the dispatcher +did not recognize the argument list and an error is raised. +Otherwise the 'generator procedure' g is used +to run <vars> through a sequence of values. +The sequence defined by g is obtained by repeated +evaluation of (g empty) +until the result is empty. +In other words, g indicates the end of the sequence by +returning its only argument, for which the caller has provided +an object distinct from anything g can produce. +(Generator procedures are state based, they are no such noble things +as streams in the sense of SRFI 40.)

    + + +The definition of dispatchers is greatly simplified by the +macro :generator-proc that constructs a generator +procedure from a typed generator. +Let (g var arg1 arg ...) be an instance of +the <generator> syntax, for example an +application-specific typed generator, +with a single variable var and no index variable. +Then + +

    +(:generator-proc (g arg1 arg ...)) => g,
    + +where the generator procedure g runs through the list +(list-ec (g var arg1 arg ...) var).

    + + +Adding an application-specific dispatching generator. +In order to define a new dispatching generator (say :my) +first a dispatching procedure (say :my-dispatch) is defined. +The dispatcher will be called with a single (!) argument +containing the list of all values to dispatch on. +To enable informative error messages, the dispatcher should +return a descriptive object (e.g. a symbol for the module name) +when it is called with the empty list. +Otherwise (if there is at least one value to dispatch on), +the dispatcher must either return a generator procedure +or #f (= no interest). +As an example, the following skeleton code defines a dispatcher +similar to the initial dispatcher of :: + +

    +(define (:my-dispatch args)
    +  (case (length args)
    +    ((0) 'SRFI-NN)
    +    ((1) (let ((a1 (car args)))
    +           (cond
    +            ((list? a1)
    +             (:generator-proc (:list a1)) )
    +            ((string? a1)
    +             (:generator-proc (:string a1)) )
    +            ...more unary cases...
    +            (else
    +             #f ))))
    +    ((2) (let ((a1 (car args)) (a2 (cadr args)))
    +           (cond
    +            ((and (list? a1) (list? a2))
    +             (:generator-proc (:list a1 a2)) )
    +            ...more binary cases...
    +            (else
    +             #f ))))
    +    ...more arity cases...
    +    (else
    +     (cond
    +      ((every?-ec (:list a args) (list? a))
    +       (:generator-proc (:list (apply append args))) )
    +      ...more large variable arity cases...
    +      (else
    +       #f )))))
    + +Once the dispatcher has been defined, the following macro +implements the new dispatching generator: + +
    +(define-syntax :my
    +  (syntax-rules (index)
    +    ((:my cc var (index i) arg1 arg ...)
    +     (:dispatched cc var (index i) :my-dispatch arg1 arg ...) )
    +    ((:my cc var arg1 arg ...)
    +     (:dispatched cc var :my-dispatch arg1 arg ...) )))
    + +This method of extension yields complete control of the dispatching process. +Other modules can only add cases to :my +if they have access to :my-dispatch.

    + + +Extending the predefined dispatched generator. +An alternative to adding a new dispatched generators is extending +the predefined generator :. +Technically, extending : means +installing a new global dispatching procedure using +:-dispatch-set! +as described above. + + +In most cases, however, the already installed dispatcher should +be extended by new cases. +The following procedure is a utility for doing so: + +

    (dispatch-union d1 d2) => d,
    + +where the new dispatcher d recognizes the union of the +cases recognized by the dispatchers d1 and d2. +The new dispatcher always tries both component dispatchers +and raises an error in case of conflict. +The identification returned by (d) +is the concatenation of the component identifications +(d1) and +(d2), enclosed in lists +if necessary. +For illustration, consider the following code: + +
    +(define (example-dispatch args)
    +  (cond
    +   ((null? args)
    +    'example )
    +   ((and (= (length args) 1) (symbol? (car args)) )
    +    (:generator-proc (:string (symbol->string (car args)))) )
    +   (else
    +    #f )))
    +
    +(:-dispatch-set! (dispatch-union (:-dispatch-ref) example-dispatch))
    + +After evaluation of this code, the following example will work: + +
    (list-ec (: c 'abc) c) => (#\a #\b #\c)
    + +Adding cases to : is particularly useful +for frequent cases of interactive input. +Be warned, however, that the advantage of global extension also carries +the danger of conflicts, unexpected side-effects, and slow dispatching. +
    +
    + +
    +
    +(:do (<lb>*) <ne1?> (<ls>*))
    +(:do (let (<ob>
    *) <oc>*) (<lb>*) <ne1?> (let (<ib>*) <ic>*) <ne2?> (<ls>*)) +
    + +
    +Defines a generator in terms of a named-let, +optionally decorated with inner and outer lets. +This generator is for defining other generators. +(In fact, the reference implementation transforms any other +generator into an instance of fully decorated :do.) +The generator is a compromise between expressive power +(more flexible loops) and fixed structure (necessary +for merging and modifying generators). +In the fully decorated form, the syntactic variables +<ob> (outer binding), +<oc> (outer command), +<lb> (loop binding), +<ne1?> (not-end1?), +<ib> (inner binding), +<ic> (inner command), +<ne2?> (not-end2?), and +<ls> (loop step) +define the following loop skeleton: + +
    +(let (<ob>*)
    +  <oc>*
    +  (let loop (<lb>*) 
    +    (if <ne1?>
    +        (let (<ib>*)
    +          <ic>*
    +          payload
    +          (if <ne2?>
    +              (loop <ls>*) ))))),
    + +where <oc>* and +<ic>* are syntactically +equivalent to <command>*, +i.e. they do not begin with a <definition>. +The latter requirement allows the code generator to +produce more efficient code for special cases by +removing empty let-expressions altogether. +
    +
    + +
    +
    +(:let <vars> <expression>) +
    + +
    +Runs through the sequence consisting of the value of +<expression>, only. +This is the same as +(:list <vars> (list <expression>)). +If an index variable is specified, its value is 0. +The :let-generator can be used to introduce +an intermediate variable depending on outer generators. +
    +
    + +
    +
    +(:parallel <generator>*) +
    + +
    +Runs several generators in parallel. +This means that the next binding in the sequence is obtained +by advancing each generator in <generator>* +by one step. +The parallel generator terminates when any of its component +generators terminates. +The generators share a common scope for the variables +they introduce. +This implies that the names of the variables introduced +by the various generators must be distinct. +
    +
    + +
    +
    +(:while <generator> <expression>) +
    + +
    +Runs <generator> while <expression> +evaluates to non-#f. +The guarding expression is included in the scope +of the variables introduced by the generator.

    + +Note the distinction between the filter if and +the modified generator expressed by :while. +

    +
    + +
    +
    +(:until <generator> <expression>) +
    + +
    +Runs <generator> until after +<expression> has evaluated to non-#f. +The guarding expression is included in the scope +of the variables introduced by the generator.

    + +Note the distinction between :while, stopping at +a certain condition, and :until, stopping after +a certain condition has occurred. The latter implies that the binding +that has triggered termination has been processed by the comprehension. +

    +
    + +
    +
    +<application-specific typed generator> +
    + +
    +An important aspect of this SRFI is a modular mechanism to +define new typed generators. +To define a new typed generator a hygienic referentially +transparent macro of the same name is defined to transform +the generator pattern into an instance of the +:do-generator. +The extension is fully modular, meaning that no other +macro has to be modified to add the new generator. +This is achieved by defining the new macro in +Continuation Passing Style, as in [MWL].

    + +Technically, this works as follows. +Assume the generator syntax (:mygen <var> <arg>) +is to be implemented, for example running the variable <var> +through the list (reverse <arg>). +The following definition implements :mygen +in terms of :list +using the additional syntactic variable cc +(read current continuation): + +

    +(define-syntax :mygen
    +  (syntax-rules ()
    +    ((:mygen cc var arg)
    +     (:list cc var (reverse arg)) )))
    + +After this definition, any comprehension will accept +the :mygen-generator and produce the +proper code for it. +This works as follows. +When a comprehension sees something of the form +(g arg ...) in the position of a +<qualifier> then it will +transform the entire comprehension into +(g (continue ...) arg ...). +This effectively 'transfers control' to the +macro g, for example :mygen. +The macro g has full control of +the transformation, but eventually it should +transform the expression into +(:do (continue ...) etc ...). +In the :mygen-example this is done +by the :list-macro. +The macro :do finally transforms +into (continue ... (:do etc ...)). +As continue has been chosen by the +macro implementing the comprehension, +it can regain control and proceed +with other qualifiers.

    + +In order to ensure consistency of new generators +with the ones defined in this SRFI, a few conventions +are in order. +Firstly, the generator patterns begin with one or more +variables followed by arguments defining the sequence. +Secondly, each generator except :do +can handle an optional index variable. +This is most easily implemented using +:parallel +together with :integers. +In case the payload generator needs an index anyhow +(e.g. :vector) +it is more efficient to add an index-variable if +none is given and to implement the indexed case. +Finally, make sure that no syntactic variable of the +generator pattern ever gets duplicated in the code +(to avoid exponential code size in nested application), +and introduce sufficient intermediate variables to +make sure expressions are evaluated at the correct time. +

    +
    + +

    +Suggestions for application-specific extensions

    + +

    Arrays in the sense of [SRFI25]

    + +In order to create an array from a sequence of elements, +a comprehension with the following syntax would be useful: + +
    (array-ec <shape> <qualifier>* <expression>).
    + +The comprehension constructs a new array of the given shape +by filling it row-major with the sequence of elements as specified +by the qualifiers. + +On the generator side, it would be most useful to have a +generator of the form + +
    (:array <vars> <arg>),
    + +running through the elements of the array in row-major. +For the optional index variable, the extension +(index <k1> <k>*) +could be defined where <k1> <k>* +are variable names indexing the various dimensions. + + +

    Random Numbers in the sense of [SRFI27]

    + +In order to create a vector or list of random numbers, +it would be convenient to have generators of the following form: + +
    +(:random-integer [ <range> [ <number> ] ] )
    +(:random-real    [ <number> ] )
    + +where <range> (default 2) indicates the range of +the integers and <number> (default infinity) +specifies how many elements are to be generated. +Derived from these basic generators, one could define several +other generators for other distributions (e.g. Gaussian). + + +

    Bitstrings in the sense of [SRFI33]

    + +As eager comprehensions are efficient, they can be useful +for operations involving strings of bits. +It could be useful to have the following comprehension: + +
    (bitwise-ec <qualifier>* <expression>),
    + +which constructs an integer from bits obtained as values +of <expression> in the ordering defined +by [SRFI33]. +In other words, if the sequence of values is +x[0], x[1], ..., x[n-1] then +the result is x[0] + x[1] 2 + ... + x[n-1] 2^(n-1). +On the generator side, a generator of the form + +
    (:bitwise <vars> <arg1> <arg>*)
    + +runs through the sequence of bits obtained by appending the +binary digits of the integers <arg1> <arg>*. + +

    Streams in the sense of [SRFI 40]

    + +It is possible to 'lazify' the eager comprehension +list-ec, +constructing a stream in the sense of [SRFI 40]. +Clearly, such a comprehension (stream-ec) +is not eager at all since it only runs the loops when results are requested. +It is also possible to define a :stream-generator with +the same API as :list but running +through streams instead of lists.

    + +For what it is worth, +the file srfi40-ec.scm implements +:stream and stream-ec and gives an example. +The implementation makes substantial use of +call-with-current-continuation to run the loop +only when necessary. +In some implementations of Scheme this may involve +considerable overhead. + +

    Reading Text Files

    + +Eager comprehensions can also be used to process files. +However, bear in mind that an eager comprehension wants +to read and process the entire file right away. +Nevertheless, these generators would be useful for +reading through the lines of a file or through the +characters of a file: + +
    +(:lines-of-file <vars> <file>)
    +(:chars-of-file <vars> [ (line <variable1>) ] [ (column <variable2>) ] <file>)
    + +Here <file> is either an input port +or a string interpreted as a filename. +In a similar fashion, generators reading from sockets defined +by URLs or other communication facilities could be defined. + +

    The Scheme shell Scsh

    + +In the Scheme-shell Scsh it could be useful to have certain +comprehensions and generators. +Candidates for comprehensions are +begin-ec, +|-ec, +||-ec, and +&&-ec. +Concerning generators, it might be useful to have +:directory running through the +records of a directory, and maybe a +sophisticated :file-match-generator +could enumerate file record in a directory structure. +Optional variables of the generators could give +convenient access frequent components of the file records +(e.g. the filename). +Another candidate is :env to run through +the environment associations. +It is left to other authors and other SRFIs to +define a useful set of comprehensions and generators +for Scsh. + + +

    Design Rationale

    + +

    What is the difference between eager and lazy comprehensions?

    + +A lazy comprehension, for example stream-of in the +sense of [SRFI 40], constructs an object +representing a sequence of values. +Only at the time these values are needed that they +are actually produced. +An eager comprehension, on the other hand, is an instruction to +run through a certain sequence of values and do something with it, +for example as in do-ec. +In other words, it is nothing more sophisticated than a loop, +potentially with a more convenient notation. +This also explains why stream-of is the most +fundamental lazy comprehension, and all others can +be formulated in terms of it, whereas the most fundamental +eager comprehension is do-ec. + + +

    Why the [outer .. inner | expr] +order of qualifiers?

    + +In principle, there are six possible orders in which the +qualifiers and the expression of a comprehension can be written. +We denote the different conventions with a pattern in which +expr denotes the expression over which the comprehension +ranges, inner denotes the generator spinning fastest, and +outer denotes the generator spinning slowest. +For example, [Haskell] and +[Python] use +[expr | outer .. inner]. +(Probably with sufficient persistence, instances for any +of the conventions can be found on the Internet.) +In addition, there is the common mathematical notation +'{f(x) | x in X}'.

    + +It is important to understand that the notational convention +does not only determine the order of enumeration but also the +scope of the variables introduced by the generators. +The scope of inner includes expr, and the +scope of outer should include inner to allow +inner generates depending on outer generators. +Eventually, the choice for a particular syntactic convention is +largely a matter of personal preferences. +However, there are a few considerations that went into the +choice made for this SRFI:

    + +1. The mathematical notation is universally known and widely used. +However, the mathematical notation denotes a set comprehension +in which the order of the qualifiers is either irrelevant or must +be deduced from the context. +For the purpose of eager comprehensions as a programming language +construct, the order does matter and a simple convention is a plus. +For these reasons, the mathematical notation as such is undesirable, +but its widespread use is in favor of +[expr | inner .. outer] and +[expr | outer .. inner].

    + +2. It is desirable to have the scope of the variables increase +into one direction, as in +[expr | inner .. outer] and +[outer .. inner | expr], and +not change direction, as in +[expr | outer .. inner] +where expr is in the scope of inner +but outer is not. +This is even more important if the syntax in Scheme +does not explicitly contain the '|'-separator.

    + +3. More complicated comprehensions with several nested generators +eventually look like nested loops and Scheme always +introduces them outer .. inner as in +do and named-let. +This is in favor of +[expr | outer .. inner] and +[outer .. inner | expr]. +Shorter comprehension may look more naturally the +other way around.

    + +Regarding these contradicting preferences, I regard +linearity in scoping (2.) most important, followed by +readability for more complicated comprehensions (3.). +This leads to [outer .. inner | expr]. +An example in Scheme-syntax is +(list-ec (: x 10) (: y x) (f x y)), +which looks acceptable to me even without similarity +to the mathematical notation. +As a downside, the convention clashes with other the +convention used in other languages (e.g. Haskell and Python). + + +

    You forgot [choose your favorite here]-ec!

    + +I tried to construct a reasonably useful set of tools +according to what [R5RS] specifies. +Nevertheless, is the choice what to include and what to +leave out eventually a matter of personal preference.

    + +When 'packing the toolbox' I went for travelling light; +this SRFI does not include everything imaginable +or even everything useful. +I oriented myself at the standard procedures +of [R5RS], +with a few omissions and additions. +A notable omission are gcd-ec and +lcm-ec because they are one-liners, +and more severely, of questionable value in practice. +A notable addition are +fold-ec and +fold3-ec, +providing a mechanism to define lots of useful one-liners. +The other notable addition is +first-ec, which +is the fundamental 'early stopping' comprehension. +It is used to define +any?-ec and +every?-ec +which are among the most frequent comprehensions.

    + +Concerning the generators, the same principle has been used. +Additions include :range +and friends because they are universally needed, and +:dispatched which is +primarily intended for implementing :. + + +

    Why is the order of enumeration specified?

    + +For the purpose of this SRFI, every generator runs through +its sequence of bindings in a well specified order, and nested +generators run through the Cartesian product in the order +of nested loops. +The purpose of this definition is making the sequence as +easily predictable as possible. + +On the other hand, many mechanisms for lazy comprehensions +do not specify the order in which the elements are enumerated. +When it comes to infinite streams, this has the great advantage +that a comprehension may interleave an inner and an outer +enumeration, a method also known as 'dove-tailing' or 'diagonalizing'. +Interleaving ensures that any value of the resulting stream is +produced after a finite amount of time, even if one or more +inner streams are infinite. + +

    Why both typed and dispatching generators?

    + +The reason for typed generators is runtime efficiency. +In fact, the code produced by :range and others +will run as fast as a hand-coded do-loop. +The primary purpose of the dispatching generator is convenience. +It comes at the price of reduced runtime performance, +both for loop iteration and startup. + +

    Why the something-ec and :type naming?

    + +The purpose of the :type convention is to keep +many common comprehensions down to one-liners. +In my opinion, the fundamental nature of eager comprehensions +justifies a single character naming convention. +The something-ec convention is primarily intended to +stay away from the widely used something-of. +It reduces confusion and conflict with related mechanisms. + +

    Why combine variable binding and sequence definition?

    + +The generators of this SRFI do two different things with +a single syntactic construct: They define a sequence of values +to enumerate and they specify a variable (within a certain +scope) to run through that sequence. +An alternative is to separate the two, for example as it +has been done in +SRFI 40.

    + +The reason for combining sequence definition and enumeration +for the purpose of this SRFI is threefold. +Firstly, sequences of values are not explicitly represented as +objects in the typed generators; the generators merely +manipulate an internal state. +Secondly, this SRFI aims at a most concise notation for +common comprehensions and reduces syntax to the naked minimum. +Thirdly, this SRFI aims at the highest possible performance for +typed generators, which is achieved if the state being manipulated +is represented by the loop variable itself. + +

    Why is (: <vars>) illegal?

    + +It is reasonable and easy to define (: <vars>) +as (:integers <vars>), +enumerating the non-negative integers. +However, it turned out that a frequent mistake in using the +eager comprehensions is to forget either the variable +or an argument for the enumeration. +As this would lead to an infinite loop (not always +equally pleasant in interactive sessions), it is not allowed. + +

    Why is there no :sequential?

    + +Just like :parallel +enumerates generators in +parallel, a :sequential generator could +enumerate a concatenation of several generator, starting +the next one when the previous has finished. +The reason for not having such a qualifier is +that the generators should use all the same variable name +and there is no hygienic and referentially transparent +way of enforcing this (or even knowing the variable). + +

    Why is there no general let-qualifier?

    + +It is easy to add let, let*, +and letrec as cases to <qualifier>. +This would allow more sophisticated local variables +and expressions than possible with +(:let <vars> <expression>) and +(begin <sequence>*). +In particular, a local <definition> +in the sense of [R5RS, 7.1.5.] would +be possible.

    + +There are two reasons for not including let +and friends as qualifiers. +The first reason concerns readability. +A qualifier of the form +(let (<binding spec>*) <body>) +only makes sense if the scope of the new variables ends at the +end of the comprehension, and not already +after <body>. +The similarity with ordinary let-expressions +would be very confusing. +The second reason concerns the design rationale. +If sophisticated let-qualifiers involving +recursion or local definitions are needed, it is likely +that eager comprehensions are being overused. +In that case it might be better to define a procedure +for the task. +So including an invitation to overuse the mechanism would +be a serious violation of the +Keep It Simple and Stupid principle. + +

    Why is there no :nested generator?

    + +The specification above defines nested +as a qualifier but :parallel +as a generator. +In particular, this makes it impossible to make parallel +generators from nested ones.

    + +This design simply reflects an implementability limitation. +All component generators of :parallel are +transformed into :do-generators +and these can be merged into a parallel generator. +However, nested generators cannot be merged easily without +losing the type of the generator, +which would seriously hurt modularity and performance. + +

    Is any?-ec eager?

    + +Yes, it is still eager because it immediately starts to +run through the sequence.

    + +In fact, the reference implementation makes sure +first-ec, +any?-ec, and +every?-ec +execute efficiently so they can be used conveniently +as in +(every?-ec (:list x my-list) (pred? x)). + +

    Why this whole :dispatched business?

    + +It is specified above that the dispatching generator, +called :, is just a special case +of :dispatched using +a global dispatching procedure. +Alternatively, a simple fixed global mechanism to extend +: could have been used. +This is much simpler but does not support the definition +of new dispatched generators.

    + +The purpose of :dispatched +and its utilities +(:generator-proc and +dispatch-union) +is the following. +Assume : is to be used inside a +module but it is essential that no other module can spoil it, +e.g. by installing a very slow dispatcher. +The recommended way to proceed in this case is to define a +local copy of the original dispatching generator +:, +for example with the following code + +

    +(define :my-dispatch
    +  (make-initial-:-dispatch) )
    +
    +(define-syntax :my
    +  (syntax-rules (index)
    +    ((:my cc var (index i) arg1 arg ...)
    +     (:dispatched cc var (index i) :my-dispatch arg1 arg ...) )
    +    ((:my cc var arg1 arg ...)
    +     (:dispatched cc var :my-dispatch arg1 arg ...) ))),
    + +and to use the new generator :my instead of +:.

    + +An alternative for the dispatching mechanism as defined in +this SRFI is the use of parameter objects in the sense of +[SRFI 39]. +The dispatching generator would then access a dynamically +scoped variable to find the dispatcher, allowing full +control over dispatching. +However, this approach does not solve the dilemma that it is +sometimes useful that : is global +and sometimes undesired. +The approach specified for this SRFI addresses this dilemma +by offering options.

    + +Another alternative for dealing with the dispatching +problem is adding an optional argument to the syntax of +: through which the dispatcher +can be passed explicitly. +However, as : has variable +arity and the identifier for the variable cannot be +distinguished from any value for a dispatcher, +this is syntactically problematic. + +

    Why is there no local mechanism for adding to :?

    + +According to [R5RS, 7.1.6.] macros can only +be defined at the level of the <program> syntax. +This implies that the scope of typed generators cannot easily be +limited to local scopes. +As typed and dispatched generators go together, +there is also no strong need for a limited scope +of dispatched generators either. +Furthermore, locally extendable dispatchers are another major +headache to those trying to understand other people's code. + +

    Why are dispatchers unary?

    + +As defined in :dispatched, +a dispatching procedure is called with a single argument being +the list of values to dispatch on. +An alternative is to apply the dispatcher to the +list of values to dispatch on, which would be more natural in Scheme.

    + +The reason for not using apply is a minor +improvement in efficiency. +Every time apply is used on a procedure of variable +arity, an object containing the argument list is allocated on +the heap. +As a dispatcher may call many other dispatchers, this will adds +to the overhead of dispatching, which is relevant in inner loops. + +

    Why are there two fold comprehensions?

    + +The reason for having two fold comprehensions +(fold-ec and +fold3-ec) is efficiency.

    + +Clearly, the more general construction is +fold3-ec +as it allows individual treatment of the empty +sequence case and the singleton sequence case. +However, this comes at the price of more book-keeping +as can be seen from the +implementation example. +As the overhead is located within inner loops, +it makes sense to define another fold comprehension +for the case where the added flexibility is not needed. +This is fold-ec.

    + +The names fold-ec and fold3-ec +have been chosen for the comprehensions in order to stay +clear any other 'fold' that may be around. + +

    Why is :char-range not defined by integer->char?

    + +The definition of :char-range +specifies a sequence of adjacent characters ordered by char<=?. +The reason for not using char->integer and +integer->char is the fact that +[R5RS, 6.3.4.] leaves it to the implementation +whether the integers representing characters are consecutive or not. +In effect, this underspecification is inherited by :char-range. + + +

    Related Work and Acknowledgements

    + +Several other proposals related to the mechanism specified here exists. +The following mechanisms are made for and in Scheme (or at least a +specific dialect thereof):

    + +First of all, the report [R5RS] of Scheme itself +defines two constructs for writing loops: do and +named-let. +Both constructs express a single loop (not nested), +possibly with several variables running in parallel, +based on explicit manipulation of the state variables. +For example (do ((x 0 (+ x 1))) ((= x 10)) (display x)) +explicitly mentions how to obtain the next binding of x.

    + +Richard Kelsey's "Macros for writing loops", [MWL] +are an extension to Scheme48 to simplify the formulation of loops. +The basic idea is to stick with a do-like syntax for +more sophisticated loop constructs, not necessarily manipulating +a state variable explicitly. +For example, (list* x '(1 2 3)) expresses an enumeration +of the variable x through the list (1 2 3) +without explicit state manipulation. +The iteration constructs of [MWL], named +iterate and reduce, +express a single (not nested) loop (iterate) or +comprehension (reduce) with any number of +parallel enumerations. +A most important feature of the [MWL]-concept +is a modular way to add sequence types (generators). +In effect, the addition of a new sequence type does not +require a modification of the existing macros. +This is achieved by carefully limiting the expressive +power of the loop constructs and by using the macros +in Continuation Passing Style to call other macros. +The [MWL]-concept, and its implementation, +were most influential for this SRFI.

    + +Another related mechanism is the library of streams recently +submitted by Phil L. Bewig as [SRFI 40]. +The library contains a data type to represent even +streams (both car and cdr potentially delayed) and +defines procedures for manipulating these streams. +Moreover, the macro stream-of defines a +lazy comprehension resulting in the stream of values of +an expression subject to generators and filters. +A fixed set of generators (lists, vector, string, port, +and naturally: streams) is supported; extending the +list of generators requires changing stream-of. +Nevertheless, modularity is high since it is easy to define +a procedure producing a stream object and this can be +used for enumeration. +The order of enumeration is left unspecified to allow +interleaving of generators (also refer to +above.) +Before Phil submitted his SRFIs, we had a short +discussion in which we clarified the semantic and syntactic +differences of our approaches. +It turned out that the mechanisms are sufficiently +different not to unify them. +The most important difference is the design rationale: +Phil created his library to support the stream-paradigm +in Scheme, inspired by the work done for Haskell and +other lazy languages, and intrigued by the beauty +of programming with infinite streams. +My work only aims at a convenient way of expressing +frequent patterns of loops in a compact way. +For what it is worth, section SRFI40-ec +contains a suggestion for extending the eager comprehension +mechanism for SRFI40-streams.

    + +Phil's work on streams and lazy comprehensions in Scheme +triggered Eli Barzilay to implement a library of eager +comprehensions for PLT-Scheme, [Eli]. +The mechanism implemented by Eli is in essence very +similar to the one proposed in this SRFI, and the two +efforts have been independent until recently. +Syntactically, Eli uses infix operators for generators, +whereas this SRFI is purely prefix, and Eli uses the +[expr | outer .. inner] convention +for nesting, whereas this SRFI uses the +[outer .. inner | expr] +convention. +Semantically, Eli's mechanism defines more flexible +loops than this SRFI. +Comprehensions are regarded as generalized collection +processes like fold and reduce. +The mechanism in this SRFI is more restricted with respect +to control flow (there is no general while) +and more extensive with respect to generators and +comprehensions. +Despite the strong conceptual similarity, the design +rationales are different. +This SRFI focuses on portability and modular extension, +whatever that may cost in terms of expressive power.

    + +Finally, I would like to thank Mike Sperber for his +encouragement to proceed with the SRFI and for several +discussions of the matter. +In particular, the dispatching mechanism evolved +rapidly during discussions with Mike. + + +

    Implementation

    + +The reference implementation focuses on portability, +performance, readability and simplicity, roughly in this order. +It is written in [R5RS]-Scheme +(including macros) extended by [SRFI 23] +(error). +The reference implementation was developed +under Scheme48 (0.57), +PLT (202, 204), and +SCM (5d7).

    + +The file ec.scm is the source of +the reference implementation. +It also contains comments concerning potential problems. +Implementors might find the file design.scm +helpful. +It contains alternative implementations of certain comprehensions +and generators in order to simplify tuning the implementation +of this SRFI for different Scheme systems.

    + +The file examples.scm contains a +collection of examples, and some code checking their results. +The purpose of most examples is detecting implementation errors, +but the section 'Less artificial examples' contains a few +real-world examples.

    + +The file timing.scm contains some +code to measure an idea of performance of the comprehensions. +A hand-coded do-loop, the typed generator +(:range n), +and the dispatching generator +(: n) +are compared. +For each loop we compare the time needed +per iteration and the time needed to construct the loop (the startup delay). +As a rule of thumb, :range is as fast (or slightly faster) +as a hand-coded do-loop per iteration and needs about a +third more time for startup (due to type checking of the argument). +The dispatching generator needs about twice the time per iteration +(due to calling the generator procedure) and needs about five times +as long for startup (due to dispatching).

    + +The file extension.scm contains +examples for adding new generators and comprehensions.

    + +

    References

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    [R5RS] +Richard Kelsey, William Clinger, and Jonathan Rees (eds.): +Revised(5) Report on the Algorithmic Language Scheme of +20 February 1998. +Higher-Order and Symbolic Computation, Vol. 11, No. 1, September 1998. + +http://schemers.org/Documents/Standards/R5RS/. +
    [MWL] +Richard Kelsey, Jonathan Rees: +The Incomplete Scheme48 Reference Manual for Release 0.57 (July 15, 2001). +Section "Macros for writing loops". +http://s48.org/0.57/manual/s48manual_49.html +
    [SRFI 1] +Olin Shivers: List Library. +http://srfi.schemers.org/srfi-1/ +
    [SRFI 23] +Stephan Houben: Error reporting mechanism +http://srfi.schemers.org/srfi-23/ +
    [SRFI 25] +Jussi Piitulainen: Multi-dimensional Array Primitives. +http://srfi.schemers.org/srfi-25/ +
    [SRFI 27] +Sebastian Egner: Sources of Random Bits. +http://srfi.schemers.org/srfi-27/ +
    [SRFI 33] +Olin Shivers: Integer Bitwise-operation Library. +http://srfi.schemers.org/srfi-33/ +
    [SRFI 39] +Marc Feeley: Parameter objects. +http://srfi.schemers.org/srfi-39/ +
    [SRFI 40] +Philip L. Bewig: A Library of Streams. +http://srfi.schemers.org/srfi-40/ +
    [Eli] +Eli Barzilay: Documentation for "misc.ss". 2002. +http://www.cs.cornell.edu/eli/Swindle/misc-doc.html#collect +
    [Folds] +John David Stone: Folds and reductions. +Posting in relation to [SRFI 1] on 8-Jan-1999. +http://srfi.schemers.org/srfi-1/mail-archive/msg00021.html +
    [Haskell] +Simon L. Peyton Jones, John Hughes: The Haskell 98 Report 1 February 1999. +Section 3.11 "List Comprehensions". +http://www.haskell.org/onlinereport/exps.html#sect3.11 +
    [Python] +Guido van Rossum, Fred L. Drake Jr. (eds.): +Python Reference Manual. +Section 5.2.4 "List displays". +Release 2.2, December 21, 2001. +http://python.org/doc/2.2/ref/lists.html +
    [SICP] +Harold Abelson, Gerald J. Sussman, Julie Sussman: +Structure and Interpretation of Computer Programs. +MIT Press, 1985. +http://mitpress.mit.edu/sicp/ +
    [IFPL] +Philip Wadler: List Comprehensions (Chapter 7). In: +Simon L. Peyton Jones: The Implementation of Functional Programming Languages. +Prentice Hall, 1987. +
    [Scheme48] +Richard Kelsey, Jonathan Rees: Scheme48 Release 0.57 (July 15, 2001). +http://s48.org/ +
    [SCM] +Aubrey Jaffer: SCM Scheme Implementation. Version 5d7 (November 27, 2002). +http://www.swiss.ai.mit.edu/~jaffer/SCM.html +
    [PLT] +PLT People: PLT Scheme, DrScheme Version 203. +http://www.plt-scheme.org/ +
    [Scsh] +Olin Shivers, Brian D. Carlstrom, Martin Gasbichler, Mike Sperber: +Scsh Reference Manual. +For scsh release 0.6.3. +http://scsh.net/ +
    + + + +

    Copyright

    +

    Copyright (C) Sebastian Egner (2003). All Rights Reserved.

    + +

    +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: +

    +

    +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. +

    +

    +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +

    + + +
    +
    Author: Sebastian Egner
    +
    Editor: Francisco Solsona
    + + +Last modified: Tue Apr 5 10:43:00 CEST 2005 + + + + diff --git a/doc/srfi-std/srfi-43.html b/doc/srfi-std/srfi-43.html new file mode 100644 index 0000000000..af42c0c436 --- /dev/null +++ b/doc/srfi-std/srfi-43.html @@ -0,0 +1,2011 @@ + + + + +SRFI 43: Vector Library + + + + + +
    + +

    Title

    +Vector library + +

    Author

    +Taylor Campbell + +

    Status

    +This SRFI is currently in ``final'' status. To see an explanation of each status that a SRFI can hold, see here. +You can access the discussion via the archive of the mailing list. +
      +
    • Received: 2003/03/26 +
    • Draft: 2003/04/03-2003/06/01 +
    • Revised: 2003/07/15 +
    • Revised: 2003/11/01 +
    • Revised: 2004/04/10 +
    • Revised: 2004/04/28 +
    • Revised: 2004/08/30 +
    • Final: 2004/10/26 +
    + +
    + +

    Table of Contents

    + + +

    1. Abstract

    +

    + This SRFI proposes a comprehensive and + complete library of vector operations accompanied by a freely + available and complete reference implementation. The reference + implementation is unencumbered by copyright, and useable with no + modifications on any Scheme system that is + R5RS-compliant. It also provides several + hooks for implementation-specific optimization as well. +

    + +

    + Because this SRFI is more of a library or module specification + than a request for additions to readers or any other internal + implementation detail, in an implementation that supports a + module or structure or package or library or unit (et cetera) + systems, these procedures should be contained in a module / + structure / package / library / unit called vector-lib. +

    + +

    2. Rationale

    +

    + R5RS provides very few list-processing + procedures, for which reason SRFI 1 + (list-lib) exists. However, + R5RS provides even fewer vector operations + — while it provides mapping, appending, et cetera + operations for lists, it specifies only nine vector manipulation + operations —: +

    + + + +

    + Many Scheme implementations provide several vector operations + beyond the miniscule set that R5RS defines (the typical + vector-append, + vector-map, et cetera), but + often these procedures have different names, take arguments in + different orders, don't take the same number of arguments, or + have some other flaw that makes them unportable. For this + reason, this SRFI is proposed. +

    + +

    + It should be noted that no vector sorting procedures are provided + by this SRFI, because there already is a SRFI for such a purpose + (SRFI 32 (sort-lib)), which + includes routines for sorting not only vectors but also lists. +

    + +

    + 3. Procedure Index +

    +

    + Here is an index of the procedures provided by this package. + Those marked by bold are provided in + R5RS and those marked by bold + italic are defined by R5RS but are + modified from their original definitions. +

    +
    +
    + · Constructors +
    +
    +
    +
    + + make-vector + vector +
    + vector-unfold + vector-unfold-right +
    + vector-copy + vector-reverse-copy +
    + vector-append + vector-concatenate +
    +
    +
    +
    + +
    + · Predicates +
    +
    +
    +
    + + vector? +
    + vector-empty? +
    + vector= +
    +
    +
    +
    + +
    + · Selectors +
    +
    +
    +
    + + vector-ref +
    + vector-length +
    +
    +
    +
    + +
    + · Iteration +
    +
    +
    +
    + + vector-fold + vector-fold-right +
    + vector-map + vector-map! +
    + vector-for-each +
    + vector-count +
    +
    +
    +
    + +
    + · Searching +
    +
    +
    +
    + + vector-index + vector-index-right +
    + vector-skip + vector-skip-right +
    + vector-binary-search +
    + vector-any + vector-every +
    +
    +
    +
    + +
    + · Mutators +
    +
    +
    +
    + + vector-set! + vector-swap! +
    + vector-fill! +
    + vector-reverse! +
    + vector-copy! + vector-reverse-copy! +
    +
    +
    +
    + +
    + · Conversion +
    +
    +
    +
    + + vector->list + reverse-vector->list +
    + list->vector + reverse-list->vector +
    +
    +
    + +

    4. Procedures

    +

    + In this section containing specifications of procedures, the + following notation is used to specify parameters and return + values: +

    +
    +
    + (f arg1 arg2 ···) + -> something
    +
    + Indicates a function f takes the parameters + arg1 arg2 + ··· and returns a value of the + type something. If something + is unspecified, then what f returns is + implementation-dependant; this SRFI does not specify what it + returns, and in order to write portable code, the return value + should be ignored. +
    +
    +
    + +
    vec
    +
    + The argument in this place must be a vector, i.e. it must + satisfy the predicate + vector?. +
    +
    +
    + +
    i, j, start, size
    +
    + The argument in this place must be a nonnegative integer, i.e. + it must satisfy the predicates integer? and either + zero? or positive?. The third case of it + indicates the index at which traversal begins; the fourth case + of it indicates the size of a vector. +
    +
    +
    + +
    end
    +
    + The argument in this place must be a positive integer, i.e. it + must satisfy the predicates integer? and + positive?. This indicates the index directly before + which traversal will stop — processing will occur until + the the index of the vector is end. It is the + closed right side of a range. +
    +
    +
    + +
    f
    +
    + The argument in this place must be a function of one or more + arguments, returning exactly one value. +
    +
    +
    + +
    pred?
    +
    + The argument in this place must be a function of one or more + arguments that returns one value, which is treated as a + boolean. +
    +
    +
    + +
    + x, y, z, seed, knil, fill, key, value +
    +
    + The argument in this place may be any Scheme value. +
    +
    +
    + +
    [something]
    +
    + Indicates that something is an optional + argument; it needn't necessarily be applied. + Something needn't necessarily be one thing; for + example, this usage of it is perfectly valid: +
    +
    + +    [start [end]] + +
    +
    + and is indeed used quite often. +
    +
    +
    + +
    something ···
    +
    + Indicates that zero or more somethings are + allowed to be arguments. +
    +
    +
    + +
    + something1 something2 + ··· +
    +
    + Indicates that at least one something must be + arguments. +
    +
    +
    + +
    + something1 something2 + ··· + somethingn +
    +
    + Exactly equivalent to the previous argument notation, but this + also indicates that n will be used later in the + procedure description. +
    +
    +
    +
    + +

    + It should be noted that all of the procedures that iterate across + multiple vectors in parallel stop iterating and produce the final + result when the end of the shortest vector is reached. The sole + exception is vector=, which + automatically returns #f if the vectors' lengths vary. +

    + +

    4.1. Constructors

    +
    +
    + + (make-vector size [fill]) + -> vector + +
    +
    + [R5RS] Creates and returns a vector + of size size, optionally filling it with + fill. The default value of + fill is unspecified. +
    +
    + Example: +
    +
    + + (make-vector 5 3) + +
    + + #(3 3 3 3 3) + +
    +
    +
    + +
    + + (vector x ···) + -> vector + +
    +
    + [R5RS] Creates and returns a vector + whose elements are x ···. +
    +
    + Example: +
    +
    + + (vector 0 1 2 3 4) + +
    + + #(0 1 2 3 4) + +
    +
    +
    + +
    + + (vector-unfold f length initial-seed + ···) + -> vector + +
    +
    + The fundamental vector constructor. Creates a vector whose + length is length and iterates across each index + k between 0 and + length, applying f at each + iteration to the current index and current seeds, in that + order, to receive n + 1 values: first, the + element to put in the kth slot of the new + vector and n new seeds for the next iteration. + It is an error for the number of seeds to vary between + iterations. +
    +
    + Examples: +
    +
    + + (vector-unfold (λ (i x) (values x (- x 1))) +
               +       + 10 0) +
    +
    + + #(0 -1 -2 -3 -4 -5 -6 -7 -8 -9) + +
    +
    + Construct a vector of the sequence of integers in the range + [0,n). +
    + + (vector-unfold values n) + +
    + + #(0 1 2 ··· n-2 n-1) + +
    +
    + Copy vector. +
    +
    + + (vector-unfold (λ (i) (vector-ref vector i)) +
               +       + (vector-length vector)) +
    +
    +
    +
    + +
    + + (vector-unfold-right f length initial-seed + ···) + -> vector + +
    +
    + Like vector-unfold, but + it uses f to generate elements from + right-to-left, rather than left-to-right. +
    +
    + Examples: +
    +
    + Construct a vector in reverse of the integers in the range + [0,n). +
    +
    + + (vector-unfold-right (λ (i x) (values x (+ x 1))) n 0) + +
    + + #(n-1 n-2 ··· 2 1 0) + +
    +
    + Reverse vector. +
    +
    + + (vector-unfold-right (λ (i x) + (values (vector-ref vector x) + (+ x 1))) +
               +             + (vector-length vector) +
               +             + 0) +
    +
    +
    +
    + + + +
    + + (vector-copy vec + [start [end [fill]]]) + -> vector + +
    +
    + Allocates a new vector whose length is end - + start and fills it with elements from + vec, taking elements from vec + starting at index start and stopping at index + end. start defaults to + 0 and end defaults to the value of + (vector-length + vec). If end extends beyond the + length of vec, the slots in the new vector that + obviously cannot be filled by elements from vec + are filled with fill, whose default value is + unspecified. +
    +
    + Examples: +
    +
    + + (vector-copy '#(a b c d e f g h i)) + +
    + + #(a b c d e f g h i) + +
    +
    + + (vector-copy '#(a b c d e f g h i) 6) + +
    + + #(g h i) + +
    +
    + + (vector-copy '#(a b c d e f g h i) 3 6) + +
    + + #(d e f) + +
    +
    + + (vector-copy '#(a b c d e f g h i) 6 12 'x) + +
    + + #(g h i x x x) + +
    +
    +
    + +
    + + (vector-reverse-copy vec + [start [end]]) + -> vector + +
    +
    + Like vector-copy, but it + copies the elements in the reverse order from + vec. +
    +
    + Example: +
    +
    + + (vector-reverse-copy '#(5 4 3 2 1 0) 1 5) + +
    + + #(1 2 3 4) + +
    +
    +
    + +
    + + (vector-append vec ···) + -> vector + +
    +
    + Returns a newly allocated vector that contains all elements in + order from the subsequent locations in vec + ···. +
    +
    + Examples: +
    +
    + + (vector-append '#(x) '#(y)) + +
    + + #(x y) + +
    +
    + + (vector-append '#(a) '#(b c d)) + +
    + + #(a b c d) + +
    +
    + + (vector-append '#(a #(b)) '#(#(c))) + +
    + + #(a #(b) #(c)) + +
    +
    +
    + +
    + + (vector-concatenate list-of-vectors) + -> vector + +
    +
    + Appends each vector in list-of-vectors. This + is equivalent to: +
    +
    + + (apply vector-append + list-of-vectors) + +
    +
    + However, it may be implemented better. +
    +
    + Example: +
    +
    + + (vector-concatenate '(#(a b) #(c d))) + +
    + + #(a b c d) + +
    +
    +
    +
    + +

    4.2. Predicates

    +
    +
    + + (vector? x) + -> boolean + +
    +
    + [R5RS] Disjoint type predicate for + vectors: this returns #t if x is a + vector, and #f if otherwise. +
    +
    + Examples: +
    +
    + + (vector? '#(a b c)) + +
    + + #t + +
    +
    + + (vector? '(a b c)) + +
    + + #f + +
    +
    + + (vector? #t) + +
    + + #f + +
    +
    + + (vector? '#()) + +
    + + #t + +
    +
    + + (vector? '()) + +
    + + #f + +
    +
    +
    + +
    + + (vector-empty? vec) + -> boolean + +
    +
    + Returns #t if vec is empty, i.e. its + length is 0, and #f if not. +
    +
    + Examples: +
    +
    + + (vector-empty? '#(a)) + +
    + + #f + +
    +
    + + (vector-empty? '#(())) + +
    + + #f + +
    +
    + + (vector-empty? '#(#())) + +
    + + #f + +
    +
    + + (vector-empty? '#()) + +
    + + #t + +
    +
    +
    + +
    + + (vector= elt=? vec ···) + -> boolean + +
    +
    + Vector structure comparator, generalized across user-specified + element comparators. Vectors a and + b are considered equal by vector= iff + their lengths are the same, and for each respective elements + Ea and + Eb, (elt=? Ea + Eb) returns a true value. + Elt=? is always applied to two arguments. + Element comparison must be consistent with eq; that + is, if (eq? Ea Eb) + results in a true value, then (elt=? Ea + Eb) must also result in a true value. + This may be exploited to avoid unnecessary element comparisons. + (The reference implementation does, but it does not consider + the situation where elt=? is in fact itself + eq? to avoid yet more unnecessary comparisons.) +
    +
    + If there are only zero or one vector arguments, #t is + automatically returned. The dynamic order in which comparisons + of elements and of vectors are performed is left completely + unspecified; do not rely on a particular order. +
    +
    + Examples: +
    +
    + + (vector= eq? '#(a b c d) '#(a b c d)) + +
    + + #t + +
    +
    + + (vector= eq? '#(a b c d) '#(a b d c)) + +
    + + #f + +
    +
    + + (vector= = '#(1 2 3 4 5) '#(1 2 3 4)) + +
    + + #f + +
    +
    + + (vector= = '#(1 2 3 4) '#(1 2 3 4)) + +
    + + #t + +
    +
    + The two trivial cases. +
    +
    + + (vector= eq?) + +
    + + #t + +
    +
    + + (vector= eq? '#(a)) + +
    + + #t + +
    +
    + Note the fact that we don't use vector literals in the next two + — it is unspecified whether or not literal vectors with + the same external representation are eq?. +
    +
    + + (vector= eq? (vector (vector 'a)) (vector (vector 'a))) + +
    + + #f + +
    +
    + + (vector= equal? (vector (vector 'a)) (vector (vector 'a))) + +
    + + #t + +
    +
    +
    +
    + +

    4.3. Selectors

    +
    +
    + + (vector-ref vec i) + -> value + +
    +
    + [R5RS] Vector element dereferencing: + returns the value that the location in vec at + i is mapped to in the store. Indexing is based + on zero. I must be within the range [0, + (vector-length + vec)). +
    +
    + Example: +
    +
    + + (vector-ref '#(a b c d) 2) + +
    + + c + +
    +
    +
    + +
    + + (vector-length vec) + -> exact nonnegative integer + +
    +
    + [R5RS] Returns the length of vec, the number of + locations reachable from vec. (The careful + word 'reachable' is used to allow for 'vector slices,' whereby + vec refers to a larger vector that contains + more locations that are unreachable from vec. + This SRFI does not define vector slices, but later SRFIs may.) +
    +
    + Example: +
    +
    + + (vector-length '#(a b c)) + +
    + + 3 + +
    +
    +
    +
    + +

    4.4. Iteration

    +
    +
    + + (vector-fold kons knil vec1 vec2 + ···) + -> value + +
    +
    + The fundamental vector iterator. Kons is + iterated over each index in all of the vectors, stopping at the + end of the shortest; kons is applied as + + (kons i state + (vector-ref + vec1 i) + (vector-ref + vec2 i) + ···) + + where state is the current state value — + the current state value begins with knil, and + becomes whatever kons returned at the + respective iteration —, and i is the + current index. +
    +
    + The iteration is strictly left-to-right. +
    +
    + Examples: +
    +
    + Find the longest string's length in + vector-of-strings. +
    + + (vector-fold (λ (index len str) + (max (string-length str) len)) +
              +      + 0 vector-of-strings) +
    +
    +
    + Produce a list of the reversed elements of + vec. +
    + + (vector-fold (λ (index tail elt) (cons elt tail)) +
              +      + '() vec) +
    +
    +
    + Count the number of even numbers in vec. +
    + + (vector-fold (λ (index counter n) +
              +        + (if (even? n) (+ counter 1) counter)) +
              +      + 0 vec) +
    +
    +
    +
    + +
    + + (vector-fold-right kons knil + vec1 vec2 + ···) + -> value + +
    +
    + Similar to vector-fold, but + it iterates right to left instead of left to right. +
    +
    + Example: +
    +
    + Convert a vector to a list. +
    + + (vector-fold-right (λ (index tail elt) + (cons elt tail)) +
              +            + '() '#(a b c d)) +
    +
    + + (a b c d) + +
    +
    +
    + +
    + + (vector-map f vec1 vec2 + ···) + -> vector + +
    +
    + Constructs a new vector of the shortest size of the vector + arguments. Each element at index i of the new + vector is mapped from the old vectors by + (f i + (vector-ref + vec1 + i) + (vector-ref + vec2 + i) + ···). + The dynamic order of application of f is + unspecified. +
    +
    + Examples: +
    +
    + + (vector-map (λ (i x) (* x x)) +
              +     + (vector-unfold + (λ (i x) (values x (+ x 1))) + 4 1)) +
    +
    + + #(1 4 9 16) + +
    +
    + + (vector-map (λ (i x y) (* x y))
    +            +    (vector-unfold + (λ (i x) (values x (+ x 1))) + 5 1)
    +            +    (vector-unfold + (λ (i x) (values x (- x 1))) + 5 5)) +
    +
    + + #(5 8 9 8 5) + +
    +
    + + (let ((count 0)) + +
    + +    + (vector-map (λ (ignored-index ignored-elt) + +
    + +            +       + (set! count (+ count 1)) + +
    + +            +       + count) + +
    + +            +     + '#(a b))) + +
    + + #(1 2) OR #(2 1) + +
    +
    + + (vector-map (λ (i elt) (+ i elt)) '#(1 2 3 4)) + +
    + + #(1 3 5 7) + +
    +
    +
    + +
    + + (vector-map! f vec1 vec2 + ···) + -> unspecified + +
    +
    + Similar to vector-map, but + rather than mapping the new elements into a new vector, the new + mapped elements are destructively inserted into + vec1. Again, the dynamic order of + application of f unspecified, so it is + dangerous for f to apply either + vector-ref or + vector-set! to + vec1 in f. +
    +
    +
    + +
    + + (vector-for-each f vec1 vec2 + ···) + -> unspecified + +
    +
    + Simple vector iterator: applies f to each index + in the range [0, length), where + length is the length of the smallest vector + argument passed, and the respective list of parallel elements + from vec1 vec2 + ··· at that index. In contrast with + vector-map, f + is reliably applied to each subsequent elements, starting at + index 0, in the vectors. +
    +
    + Example: +
    +
    + + (vector-for-each (λ (i x) (display x) (newline)) + +
    + +            +       + '#("foo" "bar" "baz" "quux" "zot")) + +
    + Displays: +
    +
    +foo
    +bar
    +baz
    +quux
    +zot
    +
    +
    +
    + +
    + + (vector-count pred? vec1 vec2 + ···) + -> exact nonnegative integer + +
    +
    + Counts the number of parallel elements in the vectors that + satisfy pred?, which is applied, for each index + i in the range [0, length) + — where length is the length of the + smallest vector argument —, to i and each + parallel element in the vectors at that index, in order. +
    +
    + Examples: +
    +
    + + (vector-count (λ (i elt) (even? elt)) + '#(3 1 4 1 5 9 2 5 6)) + +
    + + 3 + +
    +
    + + (vector-count (λ (i x y) (< x y)) + '#(1 3 6 9) '#(2 4 6 8 10 12)) + +
    + + 2 + +
    +
    +
    +
    + +

    4.5. Searching

    +
    +
    + + (vector-index pred? vec1 vec2 + ···) + -> exact nonnegative integer or #f + +
    +
    + Finds & returns the index of the first elements in + vec1 vec2 + ··· that satisfy + pred?. If no matching element is found by the + end of the shortest vector, #f is returned. +
    +
    + Examples: +
    +
    + + (vector-index even? '#(3 1 4 1 5 9)) + +
    + + 2 + +
    +
    + + (vector-index < '#(3 1 4 1 5 9 2 5 6) '#(2 7 1 8 2)) + +
    + + 1 + +
    +
    + + (vector-index = '#(3 1 4 1 5 9 2 5 6) '#(2 7 1 8 2)) + +
    + + #f + +
    +
    +
    + +
    + + (vector-index-right pred? vec1 vec2 + ···) + -> exact nonnegative integer or #f + +
    +
    + Like vector-index, but it + searches right-to-left, rather than left-to-right, and all of + the vectors must have the same length. +
    +
    +
    + +
    + + (vector-skip pred? vec1 vec2 + ···) + -> exact nonnegative integer or #f + +
    +
    + Finds & returns the index of the first elements in + vec1 vec2 + ··· that do not satisfy + pred?. If all the values in the vectors + satisfy pred? until the end of the shortest + vector, this returns #f. This is equivalent to: +
    +
    + + (vector-index + (λ (x1 x2 + ···) + (not (pred? x1 + x1 + ···))) +
              +           + vec1 vec2 + ···) +
    +
    +
    + Example: +
    +
    + + (vector-skip number? '#(1 2 a b 3 4 c d)) + +
    + + 2 + +
    +
    +
    + +
    + + (vector-skip-right pred? vec1 vec2 + ···) + -> exact nonnegative integer or #f + +
    +
    + Like vector-skip, but it + searches for a non-matching element right-to-left, rather than + left-to-right, and all of the vectors must have the same + length. This is equivalent to: +
    +
    + + (vector-index-right + (λ (x1 x2 + ···) + (not (pred? x1 + x1 + ···))) +
              +            +      + vec1 vec2 + ···) +
    +
    +
    +
    + +
    + + (vector-binary-search vec value cmp) + -> exact nonnegative integer or #f + +
    +
    + Similar to vector-index + and + vector-index-right, + but instead of searching left to right or right to left, this + performs a binary search. cmp should be a + procedure of two arguments and return a negative integer, which + indicates that its first argument is less than its second, + zero, which indicates that they are equal, or a positive + integer, which indicates that the first argument is greater + than the second argument. An example cmp might + be: +
    +
    + + (λ (char1 char2) + +
    + +   (cond ((char<? char1 + char2) + -1) + +
    + +         + ((char=? char1 + char2) + 0) + +
    + +         + (else 1))) + +
    +
    +
    + +
    + + (vector-any pred? vec1 vec2 + ···) + -> value or #f + +
    +
    + Finds the first set of elements in parallel from + vec1 vec2 + ··· for which + pred? returns a true value. If such a parallel + set of elements exists, vector-any returns the value + that pred? returned for that set of elements. + The iteration is strictly left-to-right. +
    +
    +
    + +
    + + (vector-every pred? vec1 vec2 + ···) + -> value or #f + +
    +
    + If, for every index i between 0 and the length + of the shortest vector argument, the set of elements + (vector-ref vec1 + i) + (vector-ref vec2 + i) + ··· + satisfies pred?, vector-every returns + the value that pred? returned for the last + set of elements, at the last index of the shortest vector. The + iteration is strictly left-to-right. +
    +
    +
    +
    + +

    4.6. Mutators

    +
    +
    + + (vector-set! vec i value) + -> unspecified + +
    +
    + [R5RS] Assigns the contents of the location at i in + vec to value. +
    +
    +
    + +
    + + (vector-swap! vec i j) + -> unspecified + +
    +
    + Swaps or exchanges the values of the locations in + vec at i & + j. +
    +
    +
    + +
    + + (vector-fill! vec fill [start [end]]) + -> unspecified + +
    +
    + [R5RS+] Assigns the value of every location in vec + between start, which defaults to 0 and + end, which defaults to the length of + vec, to fill. +
    +
    +
    + +
    + + (vector-reverse! vec [start [end]]) + -> unspecified + +
    +
    + Destructively reverses the contents of the sequence of + locations in vec between start + and end. Start defaults to + 0 and end defaults to the length of + vec. Note that this does not deeply reverse. +
    +
    +
    + +
    + + (vector-copy! target tstart source + [sstart [send]]) + -> unspecified + +
    +
    + Copies a block of elements from source to + target, both of which must be vectors, starting + in target at tstart and + starting in source at sstart, + ending when send - sstart elements have + been copied. It is an error for target to have + a length less than tstart + (send - + sstart). Sstart defaults to + 0 and send defaults to the length of + source. +
    +
    +
    + +
    + + (vector-reverse-copy! target tstart source + [sstart [send]]) + -> unspecified + +
    +
    + Like vector-copy!, but + this copies the elements in the reverse order. It is an error + if target and source are + identical vectors and the target & source ranges overlap; + however, if tstart = sstart, + vector-reverse-copy! behaves as + + (vector-reverse! + target + tstart + send) + + would. +
    +
    +
    +
    + +

    4.7. Conversion

    +
    +
    + + (vector->list vec [start [end]]) + -> proper-list + +
    +
    + [R5RS+] Creates a list containing the elements in vec + between start, which defaults to 0, + and end, which defaults to the length of + vec. +
    +
    +
    + +
    + + (reverse-vector->list vec + [start [end]]) + -> proper-list + +
    +
    + Like vector->list, + but the resulting list contains the elements in reverse between + the the specified range. +
    +
    +
    + +
    + + (list->vector proper-list) -> vector + +
    +
    + [R5RS+] Creates a vector of elements from proper-list. +
    +
    +
    + +
    + + (reverse-list->vector proper-list) -> vector + +
    +
    + Like list->vector, + but the resulting list contains the elements in reverse of + proper-list. +
    +
    +
    +
    + +

    + 5. Reference Implementation +

    +

    + With this SRFI comes a complete reference implementation. It is + licensed under a very open copyright with which no implementors + should have any legal issues. +
    +
    + The reference implementation has only one non-R5RS dependency: + SRFI 23's error procedure. +
    +
    + This reference implementation of all the procedures described in + this SRFI can be found here. +

    + +

    + 6. Acknowledgements +

    +

    + Thanks to Olin Shivers for his wonderfully complete + list and string + packages; to all the members of the + #scheme IRC + channel on Freenode + who nitpicked a great deal, but also helped quite a lot in + general, and helped test the reference implementation in various + Scheme systems; to Michael Burschik for his numerous comments; to + Sergei Egorov for helping to narrow down the procedures; to Mike + Sperber for putting up with an extremely overdue draft; to + Felix Winkelmann for continually bugging me about finishing up the + SRFI so that it would be only overdue and not withdrawn; and to + everyone else who gave questions, comments, thoughts, or merely + attention to the SRFI. +

    + +

    7. References

    +
    +
    R5RS
    +
    + R5RS: The Revised5 Report on Scheme +
    + R. Kelsey, W. Clinger, J. Rees (editors). +
    + Higher-Order and Symbolic Computation, Vol. 11, No. 1, + September, 1998 +
    + and +
    + ACM SIGPLAN Notices, Vol. 33, No. 9, October, 1998 +
    + Available at: + + http://www.schemers.org/Documents/Standards/R5RS/ + +
    +
    +
    + +
    SRFI
    +
    + SRFI: Scheme Request for Implementation +
    + The SRFI website can be found at: + + http://srfi.schemers.org/ + +
    + The SRFIs mentioned in this document are described later. +
    +
    +
    + +
    SRFI 1
    +
    + SRFI 1: List Library +
    + A SRFI of list processing procedures, written by Olin Shivers. +
    + Available at: + + http://srfi.schemers.org/srfi-1/ + +
    +
    +
    + +
    SRFI 13
    +
    + SRFI 13: String Library +
    + A SRFI of string processing procedures, written by Olin + Shivers. +
    + Available at: + + http://srfi.schemers.org/srfi-13/ + +
    +
    +
    + +
    SRFI 23
    +
    + SRFI 23: Error Reporting Mechanism +
    + A SRFI that defines a new primitive (error) for + reporting that an error occurred, written by Stephan Houben. +
    + Available at: + + http://srfi.schemers.org/srfi-23/ + +
    +
    +
    + +
    SRFI 32
    +
    + SRFI 32: Sort Libraries (draft) +
    + A SRFI of list and vector sorting routines, written by Olin + Shivers. +
    + Available at: + + http://srfi.schemers.org/srfi-32/ + +
    +
    + +

    8. Copyright

    +

    + Copyright (C) Taylor Campbell (2003). All rights reserved. +

    +

    + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or + sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: +

    +

    + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. +

    +

    + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. +

    +
    +
    Editor: Mike Sperber
    + + diff --git a/doc/srfi-std/srfi-45.html b/doc/srfi-std/srfi-45.html new file mode 100644 index 0000000000..df80918fae --- /dev/null +++ b/doc/srfi-std/srfi-45.html @@ -0,0 +1,785 @@ + + + + SRFI 45: Primitives for expressing iterative lazy algorithms + + + + +

    Title

    + + SRFI 45: Primitives for Expressing Iterative Lazy Algorithms + +

    Author

    + + André van Tonder + +

    Status

    + + This SRFI is currently in ``final'' status. To see an explanation + of each status that a SRFI can hold, see here. You + can access previous messages via + the archive of the mailing list. + +

    +

      +
    • Received: 2003/09/20
    • +
    • Draft: 2003/09/23-2003/12/23
    • +
    • Revised: 2003/12/20
    • +
    • Revised: 2004/03/06
    • +
    • Final: 2004/04/05
    • +
    • Bug fix: 2004/08/04
    • +
    + +

    Abstract

    + +Lazy evaluation is traditionally simulated in Scheme using +delay and force. However, these +primitives are not powerful enough to express +a large class of lazy algorithms that are iterative. +Indeed, it is folklore in the Scheme community that +typical iterative lazy algorithms written using +delay and +force will often +require unbounded memory. + +

    +Although varous modifications of delay and +force had been proposed to resolve this problem (see e.g., the + +SRFI-40 discussion list +) +they all fail some of the benchmarks provided below. To our knowledge, +the current SRFI provides the first exhaustive solution to this problem. + + +

    +As motivation, +we first explain how the usual laziness encoding using only delay +and force will break the iterative behavior of typical +algorithms that would have been properly tail-recursive +in a true lazy language, causing the computation to require unbounded memory. + +

    +The problem is then resolved by +introducing a set of three operations: +

    +    {lazy, delay, force}
    +
    +which allow the programmer to succinctly express lazy algorithms while +retaining bounded space behavior in cases that are properly tail-recursive. +A general +recipe for using these primitives is provided. An additional procedure +{eager} is provided for the construction of +eager promises in cases where efficiency is a concern. + +

    +Although this SRFI redefines delay and force, +the extension is conservative in the sense that the semantics of the subset {delay, force} in +isolation (i.e., as long as the program does not use lazy) +agrees with that in R5RS. In other words, no program that uses the +R5RS definitions of delay and force will break if those definition are +replaced by the SRFI-45 definitions of delay and force. + + + + +

    Rationale

    + +Wadler et al. in the paper +How to add laziness to a strict language without even being odd [Wad98], provide a +straightforward recipe for transforming arbitrary lazy data structures and algorithms +into a strict language using delay and force. + +

    +However, it is known (see e.g. the SRFI-40 discussion list) that this +transformation can lead to programs that suffer from unbounded space +consumption, even if the original lazy algorithm was properly tail-recursive. + +

    Example

    + +Consider the following procedure, written in a hypothetical lazy +language with Scheme syntax: + +
    +(define (stream-filter p? s)
    +  (if (null? s) '()
    +      (let ((h (car s))
    +            (t (cdr s)))
    +        (if (p? h)
    +            (cons h (stream-filter p? t))
    +            (stream-filter p? t)))))
    +
    + +According to the tranformation proposed in [Wad98], this algorithm can be espressed as follows +in Scheme: + +
    +(define (stream-filter p? s)
    +  (delay (force 
    +          (if (null? (force s)) (delay '())
    +              (let ((h (car (force s))) 
    +                    (t (cdr (force s))))
    +                (if (p? h)
    +                    (delay (cons h (stream-filter p? t)))
    +                    (stream-filter p? t)))))))
    +
    + +The recipe, which we will modify below, is as follows: +
      +
    • + wrap all constructors (e.g., '(), cons) with delay, +
    • +
    • + apply force to arguments of deconstructors (e.g., car, + cdr and null?), +
    • +
    • + wrap procedure bodies with (delay (force ...)). +
    • +
    + +

    +However, evaluating the following with a sufficiently value for +large-number +will cause a typical Scheme +implementation to run out of memory, despite the fact that +the original (lazy) algorithm was iterative, only needing tail calls to evaluate the +first element of the result stream. +

    +(define (from n)
    +  (delay (cons n (from (+ n 1)))))
    +
    +(define large-number 1000000000)
    +
    +(car (force (stream-filter (lambda (n) (= n large-number)) 
    +                           (from 0))))
    +
    + + +

    Why the space leak occurs

    + +The problem occurring in the above +stream-filter example can already +be seen in the following simple infinite loop, expressed in our hypothetical lazy +language as: + +
    +(define (loop) (loop))
    +
    + +which becomes, according to the [Wad98] transformation +
    +(define (loop) (delay (force (loop))))
    +
    + +Taking the semantics of {delay, force} +to be informally: + +
    +  (force (delay expr)) = update promise : (delay expr) 
    +                           with value of expr
    +                         return value in promise
    +
    + +we get +
    +  (force (loop)) = update promise1 : (delay (force (loop)))
    +                     with value of (force (loop))
    +                   return value in promise1
    +                 = update promise1 : (delay (force (loop)))
    +                     with value of 
    +                       update promise2 : (delay (force (loop)))
    +                         with value of (force (loop))
    +                       return value in promise2
    +                   return value in promise1
    +                 = update promise1 : (delay (force (loop)))
    +                     with value of 
    +                       update promise2 : (delay (force (loop)))
    +                         with value of 
    +                            update promise3 : (delay (force (loop)))
    +                              with value of (force (loop))
    +                            return value in promise3
    +                       return value in promise2
    +                   return value in promise1
    +                 = ...
    +
    + +We see that an ever growing sequence of pending promises builds up until the heap +is exhausted. + + +

    Why the above is not call-by-need

    + +Expressing the above algorithm in terms of {delay, force} +in fact does not correctly capture common notions of +call-by-need evaluation semantics. For example, +in a call-by-need language with naive graph reduction semantics, the above algorithm +would run in bounded space since naive graph reduction is known to be +tail-safe. For a good discussion of this issue, see e.g. R. Jones - +Tail recursion without space leaks [Jon98]. + +

    +Our problem may be regarded as analogous to graph reduction, +with promises corresponding to graph nodes and +force corresponding to reduction. As described by Jones, +one has to be careful with +the order in which nodes are evaluated and overwritten to avoid space +leaks. In our context this would correspond to the order in which +promises are evaluated and overwritten when forced. + +

    +In the above example, naive graph reduction would correspond to the +promise at the root being overwritten at each step before +the next iteration is evaluated, thus avoiding the need for a growing +sequence of unfulfilled promises representing (unnecessary) future +copy operations. + + +

    The solution

    + +The accumulation of unnecessary promises in the above examples is a consequence +of suspensions being forced in increasingly nested contexts. In order +to correctly simulate naive graph reduction +we should instead find a way of forcing tail suspensions iteratively, +each time overwriting the previous result. + + +

    +A solution to this problem exists and is described (in a different context) in +Compiling higher order languages into fully tail-recursive portable C +- Feely et al. [Fee97]. This reference introduces a method +widely known as the trampoline technique for evaluating +tail contexts iteratively. + +

    +Adapting the trampoline technique to the situation at hand, we +introduce a new primitive lazy, which behaves like +an "atomic" (delay (force ...)), and which will +replace the combination (delay (force ...)) at procedure +entry points. We also +redefine delay and force as below: + +

    +; type Promise a = lazy (Promise a) | eager a 
    +
    +(define-syntax lazy
    +  (syntax-rules ()
    +    ((lazy exp) 
    +     (box (cons 'lazy (lambda () exp))))))
    +
    +(define (eager x)
    +  (box (cons 'eager x)))
    +
    +(define-syntax delay
    +  (syntax-rules ()
    +    ((delay exp) (lazy (eager exp)))))
    +
    +(define (force promise)
    +  (let ((content (unbox promise)))
    +    (case (car content)
    +      ((eager) (cdr content))
    +      ((lazy)  (let* ((promise* ((cdr content)))        
    +                      (content  (unbox promise)))                      ; *
    +                 (if (not (eqv? (car content) 'eager))                 ; *
    +                     (begin (set-car! content (car (unbox promise*)))
    +                            (set-cdr! content (cdr (unbox promise*)))
    +                            (set-box! promise* content)))
    +                 (force promise))))))
    +
    +(*) These two lines re-fetch and check the original promise in case 
    +    the first line of the let* caused it to be forced.  For an example  
    +    where this happens, see reentrancy test 3 below.
    +
    +(define (box x) (list x))
    +(define unbox car)
    +(define set-box! set-car!)
    +
    + + + +Our example is then coded (see the full recipe below) +
    +  (define (loop) (lazy (loop)))
    +
    + +When we now evaluate (force (loop)), +the force procedure will execute a top-level loop +which will iteratively evaluate and overwrite subsequent +suspensions. + +

    +In the language of [Fee97], +the iterative loop in force plays the role of +"dispatcher". +The lazy form marks "control points" (procedure entry and +return points). +This technique is tail-safe because lazy procedures, instead of calling +other lazy procedures directly, simply return a +suspension representing a control point to be called upon the next iteration +of the dispatcher loop in force. For more details, see [FMRW]. + + +

    Specification

    + +The following macros should be provided. The semantics, which is informally +described here, should conform to that of the reference implementation below: +
      +
    • +(delay expression): +Takes an expression of arbitrary type a and returns a promise of type (Promise a) +which at some point in the future may be asked (by the force procedure) +to evaluate the expression and deliver the resulting value. +
    • +(lazy expression): +Takes an expression of type (Promise a) and returns a promise of type (Promise a) +which at some point in the future may be asked (by the force procedure) +to evaluate the expression and deliver the resulting promise. +
    + +The following procedures should be provided: +
      +
    • +(force expression): +Takes an argument of type (Promise a) and returns a value of type a as follows: +If a value of type a has been computed for the promise, this value is returned. +Otherwise, the promise is first evaluated, then overwritten by the obtained promise +or value, +and then force is again applied (iteratively) to the promise. +
    • +(eager expression): +Takes an argument of type a and returns a value of type Promise a. As opposed +to delay, the argument is evaluated eagerly. Semantically, +writing (eager expression) is equivalent to writing +
      +    (let ((value expression)) (delay value)).
      +
      +However, the former is more efficient since it does not require unnecessary +creation and evaluation of thunks. We also have the equivalence +
      +    (delay expression) = (lazy (eager expression))
      +
      +
    + + +The following reduction rules may be helpful for reasoning about +these primitives. However, they do not express the memoization and +memory usage +semantics specified above: +
    +  (force (delay expression)) -> expression
    +  (force (lazy  expression)) -> (force expression)
    +  (force (eager value))      -> value
    +
    + +

    +The typing can be succinctly expressed as follows: + +

    +
    +    type Promise a = lazy (Promise a) | eager a
    +    
    +           expression  : a
    +    ------------------------------
    +    (eager expression) : Promise a
    +    
    +           expression  : Promise a
    +    ------------------------------
    +    (lazy expression)  : Promise a  
    +
    +           expression  : a
    +    ------------------------------
    +    (delay expression) : Promise a 
    +
    +           expression  : Promise a 
    +    ------------------------------
    +    (force expression) : a 
    +
    +
    + +

    +Although this SRFI specifies an extension to the semantics of force, +the extension is conservative in the sense that the semantics of the subset {delay, force} in +isolation (i.e., as long as the program does not use lazy) +agrees with that in R5RS. + +

    Correct usage

    + +We now provide a general +recipe for using the primitives +
    +    {lazy, delay, force}
    +
    +to express lazy algorithms in Scheme. + +The transformation is best described by way of an example: Consider +again the stream-filter algorithm, expressed in a hypothetical lazy language +as +
    +(define (stream-filter p? s)
    +  (if (null? s) '()
    +      (let ((h (car s))
    +            (t (cdr s)))
    +        (if (p? h)
    +            (cons h (stream-filter p? t))
    +            (stream-filter p? t)))))
    +
    + +This algorithm can be espressed as follows +in Scheme: + +
    +(define (stream-filter p? s)
    +  (lazy
    +     (if (null? (force s)) (delay '())
    +         (let ((h (car (force s)))
    +               (t (cdr (force s))))
    +           (if (p? h)
    +               (delay (cons h (stream-filter p? t)))
    +               (stream-filter p? t))))))
    +
    + +In other words, we +
      +
    • + wrap all constructors (e.g., '(), cons) with delay, +
    • +
    • + apply force to arguments of deconstructors (e.g., car, + cdr and null?), +
    • +
    • + wrap procedure bodies with (lazy ...). +
    • +
    + +The only difference with the [Wad98] transformation described above is +in replacing the combination (delay (force ...)) with +(lazy ...) in the third rule. + +

    +More examples are included in the reference implementation below. + + + +

    Implementation

    + +The reference implementation uses the macro +mechanism of R5RS. +It does not use any other SRFI or any library. + +

    +A collection of benchmarks is provided. +These check some special cases of the mechanism defined +in this SRFI. To run them the user will need access to some way +of inspecting the runtime memory usage of the algorithms, and +a metric, left unspecified here, for deciding whether the memory +usage is bounded. A leak benchmark is passed if the memory usage +is bounded. +Passing the tests does not mean a correct implementation. + + +

    +Reference implementation +

    + +
    +;=========================================================================
    +; Boxes
    +
    +(define (box x) (list x))
    +(define unbox car)
    +(define set-box! set-car!)
    +
    +;=========================================================================
    +; Primitives for lazy evaluation:
    +
    +(define-syntax lazy
    +  (syntax-rules ()
    +    ((lazy exp)
    +     (box (cons 'lazy (lambda () exp))))))
    +
    +(define (eager x)
    +  (box (cons 'eager x)))
    +
    +(define-syntax delay
    +  (syntax-rules ()
    +    ((delay exp) (lazy (eager exp)))))
    +
    +(define (force promise)
    +  (let ((content (unbox promise)))
    +    (case (car content)
    +      ((eager) (cdr content))
    +      ((lazy)  (let* ((promise* ((cdr content)))        
    +                      (content  (unbox promise)))                      ; * 
    +                 (if (not (eqv? (car content) 'eager))                 ; *
    +                     (begin (set-car! content (car (unbox promise*)))
    +                            (set-cdr! content (cdr (unbox promise*)))
    +                            (set-box! promise* content)))
    +                 (force promise))))))
    +
    +; (*) These two lines re-fetch and check the original promise in case 
    +;     the first line of the let* caused it to be forced.  For an example  
    +;     where this happens, see reentrancy test 3 below.
    +
    +;=========================================================================
    +; TESTS AND BENCHMARKS:
    +;=========================================================================
    +
    +;=========================================================================
    +; Memoization test 1:
    +
    +(define s (delay (begin (display 'hello) 1)))
    +
    +(force s)
    +(force s)
    +               ;===> Should display 'hello once
    +
    +;=========================================================================
    +; Memoization test 2:
    +
    +(let ((s (delay (begin (display 'bonjour) 2))))
    +  (+ (force s) (force s)))
    +
    +               ;===> Should display 'bonjour once
    +
    +;=========================================================================
    +; Memoization test 3: (pointed out by Alejandro Forero Cuervo) 
    +
    +(define r (delay (begin (display 'hi) 1)))
    +(define s (lazy r))
    +(define t (lazy s))
    +
    +(force t)
    +(force r)
    +               ;===> Should display 'hi once
    +
    +;=========================================================================
    +; Memoization test 4: Stream memoization 
    +
    +(define (stream-drop s index)
    +  (lazy
    +   (if (zero? index)
    +       s
    +       (stream-drop (cdr (force s)) (- index 1)))))
    +
    +(define (ones)
    +  (delay (begin
    +           (display 'ho)
    +           (cons 1 (ones)))))
    +
    +(define s (ones))
    +
    +(car (force (stream-drop s 4)))
    +(car (force (stream-drop s 4)))
    +
    +               ;===> Should display 'ho five times
    +
    +;=========================================================================
    +; Reentrancy test 1: from R5RS
    +
    +(define count 0)
    +(define p
    +  (delay (begin (set! count (+ count 1))
    +                (if (> count x)
    +                    count
    +                    (force p)))))
    +(define x 5)
    +(force p)                     ;===>  6
    +(set! x 10)
    +(force p)                     ;===>  6
    +       
    +
    +;=========================================================================
    +; Reentrancy test 2: from SRFI 40
    +
    +(define f
    +  (let ((first? #t))
    +    (delay
    +      (if first?
    +          (begin
    +            (set! first? #f)
    +            (force f))
    +          'second))))
    +
    +(force f)                     ;===> 'second 
    +
    +;=========================================================================
    +; Reentrancy test 3: due to John Shutt
    +
    +(define q
    +  (let ((count 5))
    +    (define (get-count) count)
    +    (define p (delay (if (<= count 0)
    +                         count
    +                         (begin (set! count (- count 1))
    +                                (force p)
    +                                (set! count (+ count 2))
    +                                count))))
    +    (list get-count p)))
    +(define get-count (car q))
    +(define p (cadr q))
    +
    +(get-count)  ; =>   5
    +(force p)    ; =>   0
    +(get-count)  ; =>   10
    +
    +;=========================================================================
    +; Test leaks:  All the leak tests should run in bounded space.
    +
    +;=========================================================================
    +; Leak test 1: Infinite loop in bounded space.
    +
    +(define (loop) (lazy (loop)))
    +;(force (loop))                               ;==> bounded space
    +
    +;=========================================================================
    +; Leak test 2: Pending memos should not accumulate 
    +;              in shared structures.
    +
    +(define s (loop))
    +;(force s)                                    ;==> bounded space 
    +
    +;=========================================================================
    +; Leak test 3: Safely traversing infinite stream.
    +
    +(define (from n)
    +  (delay (cons n (from (+ n 1)))))
    +
    +(define (traverse s)
    +  (lazy (traverse (cdr (force s)))))
    +
    +;(force (traverse (from 0)))                  ;==> bounded space
    +
    +;=========================================================================
    +; Leak test 4: Safely traversing infinite stream 
    +;              while pointer to head of result exists.
    +
    +(define s (traverse (from 0)))  
    +;(force s)                                    ;==> bounded space
    +
    +;=========================================================================
    +; Convenient list deconstructor used below.
    +
    +(define-syntax match
    +  (syntax-rules ()
    +    ((match exp 
    +       (()      exp1)
    +       ((h . t) exp2))
    +     (let ((lst exp))
    +       (cond ((null? lst) exp1)
    +             ((pair? lst) (let ((h (car lst))
    +                                (t (cdr lst)))
    +                            exp2))
    +             (else 'match-error))))))
    +
    +;========================================================================
    +; Leak test 5: Naive stream-filter should run in bounded space.
    +;              Simplest case.
    +
    +(define (stream-filter p? s)
    +  (lazy (match (force s)
    +          (()      (delay '())) 
    +          ((h . t) (if (p? h)
    +                       (delay (cons h (stream-filter p? t)))
    +                       (stream-filter p? t))))))
    +
    +;(force (stream-filter (lambda (n) (= n 10000000000))
    +;                      (from 0)))
    +                                             ;==> bounded space
    +
    +;========================================================================
    +; Leak test 6: Another long traversal should run in bounded space.
    +
    +; The stream-ref procedure below does not strictly need to be lazy.  
    +; It is defined lazy for the purpose of testing safe compostion of 
    +; lazy procedures in the times3 benchmark below (previous 
    +; candidate solutions had failed this).  
    +
    +(define (stream-ref s index)
    +  (lazy
    +   (match (force s)
    +     (()      'error)
    +     ((h . t) (if (zero? index)
    +                  (delay h)
    +                  (stream-ref t (- index 1)))))))
    +
    +; Check that evenness is correctly implemented - should terminate:
    +
    +(force (stream-ref (stream-filter zero? (from 0))
    +                   0))                              ;==> 0
    +
    +(define s (stream-ref (from 0) 100000000))
    +;(force s)                                          ;==> bounded space
    +
    +;======================================================================
    +; Leak test 7: Infamous example from SRFI 40. 
    +
    +(define (times3 n)
    +  (stream-ref (stream-filter
    +               (lambda (x) (zero? (modulo x n)))
    +               (from 0))
    +              3))
    +
    +(force (times3 7))
    +;(force (times3 100000000))                        ;==> bounded space
    +
    +
    + +

    References

    + +[Wad98] +Philip Wadler, Walid Taha, and David MacQueen. +How to add laziness to a strict language, without even being odd, +Workshop on Standard ML, Baltimore, September 1998 +

    +[Jon92] +Richard Jones. Tail recursion without space leaks, Journal of Functional Programming, 2(1):73-79, January 1992 +

    +[Fee97] +Marc Feeley, James S. Miller, Guillermo J. Rozas, Jason A. Wilson, + Compiling Higher-Order Languages into Fully Tail-Recursive Portable C, +Rapport technique 1078, département d'informatique et r.o., Université de Montréal, août 1997. + + + + +

    Copyright

    + +

    Copyright (C) André van Tonder (2003). All Rights Reserved.

    + +

    +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: +

    +

    +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. +

    +

    +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +

    + +
    +
    Author: André van Tonder
    +
    Editor: Francisco Solsona
    + + +Last modified: Tue Dec 30 11:21:21 CST 2003 + + + diff --git a/doc/srfi-std/srfi-48.html b/doc/srfi-std/srfi-48.html new file mode 100644 index 0000000000..0989497790 --- /dev/null +++ b/doc/srfi-std/srfi-48.html @@ -0,0 +1,797 @@ + + + + SRFI 48: Intermediate Format Strings + + + + +

    Title

    + +SRFI 48: Intermediate Format Strings + +

    Author

    + +Ken Dickey + +

    Status

    + +This SRFI is currently in ``final'' status. To see an explanation of each status that a +SRFI can hold, see here. +You can access +previous messages via + +the archive of the mailing list. + +

      +
    • Received: 2003/11/24
    • +
    • Draft: 2003/11/25-2004/02/25
    • +
    • Revised: 2003/12/04
    • +
    • Revised: 2003/12/28
    • +
    • Final: 2004/03/02
    • +
    • Revised: 2005/06/01
    • +

    + +

    Abstract

    + +This document specifies Format Strings, a method of interpreting a Scheme string which contains a +number of format directives that are replaced with other string data according to the semantics of each directive. +This SRFI extends SRFI-28 in being more generally useful but is less general than +advanced format strings in that it does not allow, aside from ~F, for controlled positioning of text within fields. + +

    Issues

    + +

    +Some may disagree with specific escape options or return values. +For those who desire complex options as implemented by SLIB +or Common Lisp's FORMAT, an upwards compatible +"Advanced Format" SRFI should be proposed. +

    +In particular, the reference implementation given here does not accept numeric arguments +(aside from ~F). +Hence it does not support SRFI-29. +

    +It is highly desireable that baseline library code be small, attempt to +eliminiate heap allocation and bound stack usage. +This is especially important in embedded systems. +This can be accomplished by writing directly to a port, +rather than a string, by not supporting ~W or ~F, +and by replacing +(display (number->string n r) p) with a carefully written +(display:number->string n r p) which does not build intermediate strings. +

    +As this is intermediate format, it was felt that ~F and ~W are too highly useful to elide. +The ~H option is helpful to users, allows for programattic query, and makes clear which format directives are supported. +

    + +

    Rationale

    +

    +Inheriting from MacLisp, nearly all Lisp and Scheme implementations support some form of +FORMAT function with support for various numbers of format directives. +By agreeing to the options here, we raise the bar for portable code. +

    +The reference implementation is R5RS compliant and easy to port. +In not requiring advanced features (aside from ~W and ~F) small implementations are possible. +E.g. the reference code does not use side effects (assignment) and is less than +a third the source size of the latest SLIB implementation of FORMAT +(less than a tenth if ~F support is elided). +

    +The optional port argument allows for compatibility with older code +written for, e.g. scheme48, MIT Scheme, T, et cetera, which required a port argument. +It is also useful in cases where a synoptic +implementation of Scheme and CommonLisp is maintained. +

    + +

    Specification

    + + format [port] format-string [obj ...] + +
    +

    Accepts a format template (a Scheme String), and + processes it, replacing any format directives in order with + one or more characters, the characters themselves dependent + on the semantics of the format directive encountered. Each directive + may consume one obj. It is an error if fewer or more obj values are + provided than format directives that require them.

    + +

    When a port is specified it must be either an output port or + a boolean. If an output-port is specified, the formatted output is + output into that port. If the port argument is #t, output is to the + current-output-port. If the port is #f or no port is specified, the + output is returned as a string. If the port is specified and is #t + or an output-port, the result of the format function is unspecified.

    + +

    It is unspecified which encoding is used (e.g. ASCII, EBCDIC, UNICODE). + A given implementation must specify which encoding is used. + The implementation may or may not allow the encoding to be selected or changed.

    + +

    It is an error if an format directive consumes an obj argument and + that argument does not + confirm to a required type as noted in the table below.

    + +

    It is permissible, but highly discouraged, to implement pretty-print as + (define pretty-print write).

    + +

    An format directive is a two character sequence in the + string where the first character is a tilde '~'. Directive characters + are case-independent, i.e. upper and lower case characters + are interpreted the same. Each directive + code's meaning is described in the following table:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    DIRECTIVEMNEMONICACTIONCONSUMES? +
    ~aAny(display obj) for humansyes
    ~sSlashified(write obj) for parsersyes
    ~wWriteCircular(write-with-shared-structure obj) like ~s, but handles recursive structuresyes
    ~dDecimalthe obj is a number which is output in decimal radixyes
    ~xheXadecimalthe obj is a number which is output in hexdecimal radixyes
    ~oOctalthe obj is a number which is output in octal radixyes
    ~bBinarythe obj is a number which is output in binary radixyes
    ~cCharacterthe single charater obj is output by write-charyes
    ~yYuppifythe list obj is pretty-printed to the outputyes
    ~?Indirectionthe obj is another format-string and the following obj is a list of arguments; format is called recursivelyyes
    ~KIndirectionthe same as ~? for backward compatability with some existing implementationsyes
    ~[w[,d]]FFixed~w,dF outputs a number with width w and d digits after the decimal; + ~wF outputs a string or number with width w.yes
    ~~Tildeoutput a tildeno
    ~tTaboutput a tab characterno
    ~%Newlineoutput a newline characterno
    ~&Freshlineoutput a newline character if it is known that the previous output was not a newlineno
    ~_Spacea single space character is outputno
    ~hHelpoutputs one line of call synopsis, one line of comment, and one line of +synopsis for each format directive, starting with the directive (e.g. "~t") + no
    +
    +

    + The ~F, fixed format, directive requires some elucidation. +

    + ~wF is useful for strings or numbers. Where the string (or number->string + of the number) has fewer characters than the integer width w, the string is + padded on the left with space characters. +

    + ~w,dF is typically used only on numbers. For strings, the d + specifier is ignored. For numbers, the integer d specifies the number + of decimal digits after the decimal place. Both w and d must be + zero or positive. +

    + If d is specified, the number is processed + as if added to 0.0, i.e. it is converted to an inexact value. +

    (format "~8,2F" 1/3) => "    0.33"
    + If no d is specified, the number is not coerced to inexact. +
    (format "~6F" 32) => "    32"
    + Digits are padded to the right with zeros +
    (format "~8,2F" 32) => "   32.00"
    + If the number it too large to fit in the width specified, + a string longer than the width is returned +
    (format "~1,2F" 4321) => "4321.00"
    + If the number is complex, d is applied to both real and imaginal parts +
    (format "~1,2F" (sqrt -3.9)) => "0.00+1.97i"
    +

    + For very large or very small numbers, the point where exponential notation + is used is implementation defined. +

    (format "~8F" 32e5) => "   3.2e6" or "3200000.0"
    +

    +
    +
    +

    Examples

    +
    +(format "~h")
    +; => 
    +"(format [<port>] <format-string> [<arg>...]) -- <port> is #t, #f or an output-port
    +OPTION	[MNEMONIC]	DESCRIPTION	-- This implementation Assumes ASCII Text Encoding
    +~H	[Help]		output this text
    +~A	[Any]		(display arg) for humans
    +~S	[Slashified]	(write arg) for parsers
    +~~	[tilde]		output a tilde
    +~T	[Tab]		output a tab character
    +~%	[Newline]	output a newline character
    +~&	[Freshline]	output a newline character if the previous output was not a newline
    +~D	[Decimal]	the arg is a number which is output in decimal radix
    +~X	[heXadecimal]	the arg is a number which is output in hexdecimal radix
    +~O	[Octal]		the arg is a number which is output in octal radix
    +~B	[Binary]	the arg is a number which is output in binary radix
    +~w,dF	[Fixed]		the arg is a string or number which has width w and d digits after the decimal
    +~C	[Character]	charater arg is output by write-char
    +~_	[Space]		a single space character is output
    +~Y	[Yuppify]	the list arg is pretty-printed to the output
    +~?	[Indirection]	recursive format: next arg is a format-string and the following arg a list of arguments
    +~K	[Indirection]	same as ~?
    +"
    +
    +(format "Hello, ~a" "World!")
    +; => "Hello, World!"
    +
    +(format "Error, list is too short: ~s" '(one "two" 3))
    +; => "Error, list is too short: (one \"two\" 3))"
    +
    +(format "test me")
    +; => "test me"
    +
    +(format "~a ~s ~a ~s" 'this 'is "a" "test")
    +; => "this is a \"test\""
    +
    +(format #t "#d~d #x~x #o~o #b~b~%" 32 32 32 32)
    +;; Prints:   #d32 #x20 #o40 #b100000
    +; => <unspecified>
    +
    +(format "~a ~? ~a" 'a "~s" '(new) 'test)
    +; =>"a new test"
    +
    +(format #f "~&1~&~&2~&~&~&3~%")
    +; => 
    +"
    +1
    +2
    +3
    +"
    +
    +(format #f "~a ~? ~a ~%" 3 " ~s ~s " '(2 2) 3)
    +; => 
    +"3  2 2  3 
    +"
    +
    +(format "~w" (let ( (c '(a b c)) ) (set-cdr! (cddr c) c) c))
    +; => "#1=(a b c . #1#)"
    +
    +(format "~8,2F" 32)
    +; => "   32.00"
    +
    +(format "~8,3F" (sqrt -3.8))
    +; => "0.000+1.949i"
    +
    +(format "~8,2F" 3.4567e11)
    +; => " 3.45e11"
    +
    +(format "~6,3F" 1/3)
    +; => " 0.333"
    +
    +(format "~4F" 12)
    +; => "  12"
    +
    +(format "~8,3F" 123.3456)
    +; => " 123.346"
    +
    + (format "~6,3F" 123.3456)
    +; => "123.346"
    +
    + (format "~2,3F" 123.3456)
    +; => "123.346"
    +
    +(format "~8,3F" "foo")
    +; => "     foo"
    +
    +(format "~a~a~&" (list->string (list #\newline)) "")
    +; =>
    +"
    +"
    +
    +
    + + + +

    Implementation

    + + The implementation below requires SRFI-6 (Basic string ports), + SRFI-23 (Error reporting mechanism) and + SRFI-38 (External Representation for Data With Shared Structure).
    + + +
    +
    +;; IMPLEMENTATION DEPENDENT options
    +
    +(define ascii-tab   (integer->char  9))  ;; NB: assumes ASCII encoding
    +(define dont-print  (if (eq? #t #f) 1))
    +;;(define DONT-PRINT (string->symbol ""))
    +;;(define DONT-PRINT (void))
    +;;(define DONT-PRINT #!void)
    +(define pretty-print   write) ; ugly but permitted
    +;; (require 'srfi-38)  ;; write-with-shared-structure
    +
    +
    +;; FORMAT
    +(define (format . args)
    +  (cond
    +   ((null? args)
    +    (error "FORMAT: required format-string argument is missing")
    +    )
    +   ((string? (car args))
    +    (apply format (cons #f args)))
    +   ((< (length args) 2)
    +    (error (format #f "FORMAT: too few arguments ~s" (cons 'format args)))
    +    )
    +   (else
    +    (let ( (output-port   (car  args))
    +           (format-string (cadr args))
    +           (args          (cddr args))
    +         )
    +      (letrec ( (port 
    +                 (cond ((output-port? output-port) output-port)
    +                       ((eq? output-port #t) (current-output-port)) 
    +                       ((eq? output-port #f) (open-output-string)) 
    +                       (else (error
    +                              (format #f "FORMAT: bad output-port argument: ~s"
    +                                      output-port)))
    +                ) )
    +                (return-value 
    +                 (if (eq? output-port #f)    ;; if format into a string 
    +                     (lambda () (get-output-string port)) ;; then return the string
    +                     (lambda () dont-print)) ;; else do something harmless
    +                 )        
    +             )  
    +
    +         (define (string-index str c)
    +           (let ( (len (string-length str)) )
    +             (let loop ( (i 0) )
    +               (cond ((= i len) #f)
    +                     ((eqv? c (string-ref str i)) i)
    +                     (else (loop (+ i 1)))))))
    +
    +         (define (string-grow str len char)
    +           (let ( (off (- len (string-length str))) )
    +             (if (positive? off)
    +               (string-append (make-string off char) str)
    +               str)))
    +
    +         (define (compose-with-digits digits pre-str frac-str exp-str)
    +           (let ( (frac-len (string-length frac-str)) )
    +             (cond
    +              ((< frac-len digits) ;; grow frac part, pad with zeros
    +               (string-append pre-str "."
    +                              frac-str (make-string (- digits frac-len) #\0)
    +                              exp-str)
    +               )
    +              ((= frac-len digits) ;; frac-part is exactly the right size
    +               (string-append pre-str "."
    +                              frac-str
    +                              exp-str)
    +               )
    +              (else ;; must round to shrink it
    +               (let* ( (first-part (substring frac-str 0 digits))
    +                       (last-part  (substring frac-str digits frac-len))
    +                       (temp-str
    +                        (number->string
    +                         (round (string->number
    +                                 (string-append first-part "." last-part)))))
    +                       (dot-pos (string-index  temp-str #\.))
    +                       (carry?
    +                        (and (> dot-pos digits)
    +                             (> (round (string->number
    +                                        (string-append "0." frac-str)))
    +                                0)))
    +                       (new-frac
    +                        (substring temp-str 0 digits))
    +                     )
    +                 (string-append
    +                  (if carry? (number->string (+ 1 (string->number pre-str))) pre-str)
    +                  "."
    +                  new-frac
    +                  exp-str)))
    +         ) ) )
    + 
    +         (define (format-fixed number-or-string width digits) ; returns a string
    +           (cond
    +            ((string? number-or-string)
    +             (string-grow number-or-string width #\space)
    +             )
    +            ((number? number-or-string)
    +             (let ( (real (real-part number-or-string))
    +                    (imag (imag-part number-or-string))
    +                  )
    +               (cond
    +                ((not (zero? imag))
    +                 (string-grow
    +                  (string-append (format-fixed real 0 digits)
    +                                 (if (negative? imag) "" "+")
    +                                 (format-fixed imag 0 digits)
    +                                 "i")
    +                  width
    +                  #\space)
    +                 )
    +                (digits
    +                 (let* ( (num-str   (number->string (exact->inexact real)))
    +                         (dot-index (string-index  num-str #\.))
    +                         (exp-index (string-index  num-str #\e))
    +                         (length    (string-length num-str))
    +                         (pre-string
    +                          (cond
    +                           (exp-index
    +                            (if dot-index
    +                                (substring num-str 0 dot-index)
    +                                (substring num-str 0 (+ exp-index 1)))
    +                            )
    +                           (dot-index
    +                            (substring num-str 0 dot-index)
    +                            )
    +                           (else
    +                            num-str))
    +                          )
    +                         (exp-string
    +                          (if exp-index (substring num-str exp-index length) "")
    +                          )
    +                         (frac-string
    +                          (if exp-index
    +                              (substring num-str (+ dot-index 1) exp-index)
    +                              (substring num-str (+ dot-index 1) length))
    +                          )
    +                       )
    +                   (string-grow
    +                    (if dot-index
    +                        (compose-with-digits digits
    +                                             pre-string
    +                                             frac-string
    +                                             exp-string)
    +                        (string-append pre-string exp-string))
    +                    width
    +                    #\space)
    +                 ))
    +                (else ;; no digits
    +                 (string-grow (number->string real) width #\space)))
    +             ))
    +            (else
    +             (error
    +              (format "FORMAT: ~F requires a number or a string, got ~s" number-or-string)))
    +            ))
    +
    +         (define documentation-string
    +"(format [<port>] <format-string> [<arg>...]) -- <port> is #t, #f or an output-port
    +OPTION  [MNEMONIC]      DESCRIPTION     -- Implementation Assumes ASCII Text Encoding
    +~H      [Help]          output this text
    +~A      [Any]           (display arg) for humans
    +~S      [Slashified]    (write arg) for parsers
    +~W      [WriteCircular] like ~s but outputs circular and recursive data structures
    +~~      [tilde]         output a tilde
    +~T      [Tab]           output a tab character
    +~%      [Newline]       output a newline character
    +~&      [Freshline]     output a newline character if the previous output was not a newline
    +~D      [Decimal]       the arg is a number which is output in decimal radix
    +~X      [heXadecimal]   the arg is a number which is output in hexdecimal radix
    +~O      [Octal]         the arg is a number which is output in octal radix
    +~B      [Binary]        the arg is a number which is output in binary radix
    +~w,dF   [Fixed]         the arg is a string or number which has width w and d digits after the decimal
    +~C      [Character]     charater arg is output by write-char
    +~_      [Space]         a single space character is output
    +~Y      [Yuppify]       the list arg is pretty-printed to the output
    +~?      [Indirection]   recursive format: next 2 args are format-string and list of arguments
    +~K      [Indirection]   same as ~?
    +"
    +          )
    +
    +         (define (require-an-arg args)
    +           (if (null? args)
    +               (error "FORMAT: too few arguments" ))
    +         )
    +        
    +         (define (format-help format-strg arglist)
    +          
    +          (letrec (
    +             (length-of-format-string (string-length format-strg))
    +             
    +             (anychar-dispatch       
    +              (lambda (pos arglist last-was-newline) 
    +                (if (>= pos length-of-format-string) 
    +                  arglist ; return unused args 
    +                  (let ( (char (string-ref format-strg pos)) ) 
    +                    (cond            
    +                     ((eqv? char #\~)   
    +                      (tilde-dispatch (+ pos 1) arglist last-was-newline)) 
    +                     (else                   
    +                      (write-char char port)     
    +                      (anychar-dispatch (+ pos 1) arglist #f)        
    +                      ))               
    +                    ))        
    +             )) ; end anychar-dispatch
    +
    +             (has-newline?
    +              (lambda (whatever last-was-newline)
    +                (or (eqv? whatever #\newline)
    +                    (and (string? whatever)
    +                         (let ( (len (string-length whatever)) )
    +                           (if (zero? len)
    +                               last-was-newline
    +                               (eqv? #\newline (string-ref whatever (- len 1)))))))
    +              )) ; end has-newline?
    +             
    +             (tilde-dispatch          
    +              (lambda (pos arglist last-was-newline)     
    +                (cond           
    +                 ((>= pos length-of-format-string)   
    +                  (write-char #\~ port) ; tilde at end of string is just output
    +                  arglist ; return unused args
    +                  )     
    +                 (else      
    +                  (case (char-upcase (string-ref format-strg pos)) 
    +                    ((#\A)       ; Any -- for humans
    +                     (require-an-arg arglist)
    +                     (let ( (whatever (car arglist)) )
    +                       (display whatever port)
    +                       (anychar-dispatch (+ pos 1) 
    +                                         (cdr arglist) 
    +                                         (has-newline? whatever last-was-newline))
    +                     ))
    +                    ((#\S)       ; Slashified -- for parsers
    +                     (require-an-arg arglist)
    +                     (let ( (whatever (car arglist)) )
    +                        (write whatever port)     
    +                        (anychar-dispatch (+ pos 1) 
    +                                          (cdr arglist) 
    +                                          (has-newline? whatever last-was-newline)) 
    +                     ))
    +                    ((#\W)
    +                     (require-an-arg arglist)
    +                     (let ( (whatever (car arglist)) )
    +                        (write-with-shared-structure whatever port)  ;; srfi-38
    +                        (anychar-dispatch (+ pos 1) 
    +                                          (cdr arglist) 
    +                                          (has-newline? whatever last-was-newline))
    +                     ))                           
    +                    ((#\D)       ; Decimal
    +                     (require-an-arg arglist)
    +                     (display (number->string (car arglist) 10) port)  
    +                     (anychar-dispatch (+ pos 1) (cdr arglist) #f)  
    +                     )            
    +                    ((#\X)       ; HeXadecimal
    +                     (require-an-arg arglist)
    +                     (display (number->string (car arglist) 16) port)
    +                     (anychar-dispatch (+ pos 1) (cdr arglist) #f)  
    +                     )             
    +                    ((#\O)       ; Octal
    +                     (require-an-arg arglist)
    +                     (display (number->string (car arglist)  8) port) 
    +                     (anychar-dispatch (+ pos 1) (cdr arglist) #f) 
    +                     )       
    +                    ((#\B)       ; Binary
    +                     (require-an-arg arglist)
    +                     (display (number->string (car arglist)  2) port)
    +                     (anychar-dispatch (+ pos 1) (cdr arglist) #f) 
    +                     )           
    +                    ((#\C)       ; Character
    +                     (require-an-arg arglist)
    +                     (write-char (car arglist) port) 
    +                     (anychar-dispatch (+ pos 1) (cdr arglist) (eqv? (car arglist) #\newline))  
    +                     )          
    +                    ((#\~)       ; Tilde  
    +                     (write-char #\~ port)   
    +                     (anychar-dispatch (+ pos 1) arglist #f) 
    +                     )            
    +                    ((#\%)       ; Newline   
    +                     (newline port) 
    +                     (anychar-dispatch (+ pos 1) arglist #t) 
    +                     )
    +                    ((#\&)      ; Freshline
    +                     (if (not last-was-newline) ;; (unless last-was-newline ..
    +                         (newline port))
    +                     (anychar-dispatch (+ pos 1) arglist #t)
    +                     )
    +                    ((#\_)       ; Space 
    +                     (write-char #\space port)   
    +                     (anychar-dispatch (+ pos 1) arglist #f)
    +                     )             
    +                    ((#\T)       ; Tab -- IMPLEMENTATION DEPENDENT ENCODING    
    +                     (write-char ascii-tab port)          
    +                     (anychar-dispatch (+ pos 1) arglist #f)     
    +                     )             
    +                    ((#\Y)       ; Pretty-print
    +                     (pretty-print (car arglist) port)  ;; IMPLEMENTATION DEPENDENT
    +                     (anychar-dispatch (+ pos 1) (cdr arglist) #f)
    +                     )              
    +                    ((#\F)
    +                     (require-an-arg arglist)
    +                     (display (format-fixed (car arglist) 0 #f) port)
    +                     (anychar-dispatch (+ pos 1) (cdr arglist) #f)
    +                     )
    +                    ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) ;; gather "~w[,d]F" w and d digits
    +                     (let loop ( (index (+ pos 1))
    +                                 (w-digits (list (string-ref format-strg pos)))
    +                                 (d-digits '())
    +                                 (in-width? #t)
    +                               )
    +                       (if (>= index length-of-format-string)
    +                           (error
    +                            (format "FORMAT: improper numeric format directive in ~s" format-strg))
    +                           (let ( (next-char (string-ref format-strg index)) )
    +                             (cond
    +                              ((char-numeric? next-char)
    +                               (if in-width?
    +                                   (loop (+ index 1)
    +                                         (cons next-char w-digits)
    +                                         d-digits
    +                                         in-width?)
    +                                   (loop (+ index 1)
    +                                         w-digits
    +                                         (cons next-char d-digits)
    +                                         in-width?))
    +                               )
    +                              ((char=? next-char #\F)
    +                               (let ( (width  (string->number (list->string (reverse w-digits))))
    +                                      (digits (if (zero? (length d-digits))
    +                                                  #f
    +                                                  (string->number (list->string (reverse d-digits)))))
    +                                    )
    +                                 (display (format-fixed (car arglist) width digits) port)
    +                                 (anychar-dispatch (+ index 1) (cdr arglist) #f))
    +                               )
    +                              ((char=? next-char #\,)
    +                               (if in-width?
    +                                   (loop (+ index 1)
    +                                         w-digits
    +                                         d-digits
    +                                         #f)
    +                                   (error
    +                                    (format "FORMAT: too many commas in directive ~s" format-strg)))
    +                               )
    +                              (else
    +                               (error (format "FORMAT: ~~w.dF directive ill-formed in ~s" format-strg))))))
    +                     ))
    +                    ((#\? #\K)       ; indirection -- take next arg as format string
    +                     (cond           ;  and following arg as list of format args
    +                      ((< (length arglist) 2)
    +                       (error
    +                        (format "FORMAT: less arguments than specified for ~~?: ~s" arglist))
    +                       )
    +                      ((not (string? (car arglist)))
    +                       (error
    +                        (format "FORMAT: ~~? requires a string: ~s" (car arglist)))
    +                       )
    +                      (else
    +                       (format-help (car arglist) (cadr arglist))
    +                       (anychar-dispatch (+ pos 1) (cddr arglist) #f)
    +                     )))
    +                    ((#\H)      ; Help
    +                     (display documentation-string port)
    +                     (anychar-dispatch (+ pos 1) arglist #t)
    +                     )
    +                    (else                
    +                     (error (format "FORMAT: unknown tilde escape: ~s"
    +                                    (string-ref format-strg pos))))
    +                    )))
    +                )) ; end tilde-dispatch   
    +             ) ; end letrec            
    +            
    +             ; format-help main      
    +             (anychar-dispatch 0 arglist #f) 
    +            )) ; end format-help    
    +        
    +        ; format main
    +        (let ( (unused-args (format-help format-string args)) )
    +          (if (not (null? unused-args))
    +              (error
    +               (format "FORMAT: unused arguments ~s" unused-args)))
    +          (return-value))
    +                                              
    +      )) ; end letrec, if
    +)))  ; end format
    +
    +
    + + +

    Copyright

    +

    Copyright (C) Kenneth A Dickey (2003). All Rights Reserved.

    +

    +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: +

    +

    +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. +

    +

    +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +

    + +
    +
    Author: Ken Dickey
    +
    Editor: Francisco Solsona
    + + +Last modified: Wed Jun 01 10:40:09 CST 2005 + + + diff --git a/doc/srfi-std/srfi-5.html b/doc/srfi-std/srfi-5.html new file mode 100644 index 0000000000..594031f890 --- /dev/null +++ b/doc/srfi-std/srfi-5.html @@ -0,0 +1,345 @@ + + + + SRFI 5: A compatible let form with signatures and rest arguments + + + +

    Title

    + +SRFI-5: A compatible let form with signatures and rest arguments + +

    Author

    + +Andy Gaynor + +

    Status

    + +This SRFI is currently in ``final'' status. To see an explanation of each status that a SRFI can hold, see here. +You can access the discussion on this SRFI via the archive of the mailing list. +

      +
    • Received: 1999/2/2 +
    • Draft: 1999/2/10-1999/04/12 +
    • Final: 1999/4/26 +
    • Revised reference implementation: 2003/01/27 +
    + +

    Abstract

    + +The named-let incarnation of the let form has two slight +inconsistencies with the define form. As defined, the let +form makes no accommodation for rest arguments, an issue of functionality +and consistency. As defined, the let form does not accommodate +signature-style syntax, an issue of aesthetics and consistency. Both +issues are addressed here in a manner which is compatible with the traditional +let form but for minor extensions. + +

    Rationale

    + +

    Signature-style Syntax

    + +Consider the following two equivalent definitions: + +

    +(define fibonacci
    +  (lambda (n i f0 f1)
    +    (if (= i n)
    +      f0
    +      (fibonacci n (+ i 1) f1 (+ f0 f1)))))
    +
    +(define (fibonacci n i f0 f1)
    +  (if (= i n)
    +    f0
    +    (fibonacci n (+ i 1) f1 (+ f0 f1))))
    +
    + +Although there is a named-let analog for the former form, there is none +for the latter. To wit, suppose one wished to compute the 10th element +of the Fibonacci sequence using a named let: + +

    +

    +(let fibonacci ((n 10) (i 0) (f0 0) (f1 1))
    +  (if (= i n)
    +    f0
    +    (fibonacci n (+ i 1) f1 (+ f0 f1))))
    +Values: 55
    +
    + +As it stands, one cannot equivalently write + +

    +

    +(let (fibonacci (n 10) (i 0) (f0 0) (f1 1))
    +  (if (= i n)
    +    f0
    +    (fibonacci n (+ i 1) f1 (+ f0 f1))))
    +
    + +which is consistent with define's signature-style form. +

    Those that favor the signature style may prefer this extension. +In any case, it may be more appropriate to include all bound names within +the binding section. As presented, this straightforward extension +introduces no ambiguity or incompatibility with the existing definition +of let. + +

    Rest Arguments

    + +As it stands, one cannot write a named let with rest arguments, as in + +

    +

    +(let (blast (port (current-output-port)) . (x (+ 1 2) 4 5))
    +  (if (null? x)
    +    'just-a-silly-contrived-example
    +    (begin
    +      (write (car x) port)
    +      (apply blast port (cdr x)))))
    +
    + +otherwise equivalent to + +

    +

    +(letrec ((blast (lambda (port . x)
    +		  (if (null? x)
    +		      'just-a-silly-contrived-example
    +		      (begin
    +			(write (car x) port)
    +			(apply blast port (cdr x)))))))
    +  (blast (current-output-port) (+ 1 2) 4 5))
    +
    + +While this example is rather contrived, the functionality is not. +There are several times when the author has used this construct in practice. +Regardless, there is little reason to deny the let form access to +all the features of lambda functionality. + +

    Symbols in Binding Sections

    + +Both the features above rely upon the placement of symbols in let +binding lists (this statement is intentially simplistic). The only +other apparent use of such symbol placement is to tersely bind variables +to unspecified values. For example, one might desire to use +(let (foo bar baz) ...) +to bind foo, bar, and baz to +unspecified values. + +

    This usage is considered less important in light of the rationales +presented above, and an alternate syntax is immediately apparent, as +in (let ((foo) (bar) (baz)) ...) This may even +be preferable, consistently parenthesizing normal binding clauses. + +

    Specification

    + +

    Syntax

    + +

    +A formal specification of the syntax follows. Below, body, expression, +and identifier are free. Each instantiation of binding-name must be +unique. +

    + +

    +

    +            let = "(" "let" let-bindings body ")"
    +    expressions = nothing | expression expressions
    +   let-bindings = let-name bindings
    +                | "(" let-name "." bindings ")"
    +       let-name = identifier
    +       bindings = "(" ")"
    +                | rest-binding
    +                | "(" normal-bindings ["." rest-binding] ")"
    +normal-bindings = nothing
    +                | normal-binding normal-bindings
    + normal-binding = "(" binding-name expression ")"
    +   binding-name = identifier
    +   rest-binding = "(" binding-name expressions ")"
    +
    + +

    +For clarity and convenience, an informal specification follows. +

    + +
      +
    1. Unnamed + +

      +(let ((<parameter> <argument>)...) 
      +  <body>...)
      +
      +
    2. + +
    3. +Named, non-signature-style, no rest argument + +

      +(let <name> ((<parameter> <argument>)...)
      +  <body>...)
      +
      +
    4. + +
    5. Named, signature-style, no rest argument + +

      +(let (<name> (<parameter> <argument>)...)
      +  <body>...)
      +
      +
    6. + +
    7. Named, non-signature-style, rest argument + +

      +(let <name> ((<parameter> <argument>)...
      +    
      +. (<rest-parameter> <rest-argument>...))
      +  <body>...)
      +
      + +
    8. Named, signature-style, rest argument + +

      +(let (<name> (<parameter> <argument>)...
      +   
      +. (<rest-parameter> <rest-argument>...))
      +  <body>...)
      +
      +
    9. +
    + +

    Semantics

    + +Let $lambda and $letrec be hygienic bindings for the lambda +and letrec forms, respectively. + +
      +
    • For informal syntax 1: + +

      +(($lambda (<parameter>...) <body>...) <argument>...)
      +
      +
    • + +
    • For informal syntaxes 2 and 3: + +

      +

      +($letrec ((<name> ($lambda (<parameter>...) <body>...)))
      +  (<name> <argument>...))
      +
      +
    • + +
    • For informal syntaxes 4 and 5: + +

      +

      +($letrec ((<name> ($lambda (<parameter>...
      +   
      +. <rest-parameter>) <body>...))) 
      +  (<name> <argument>... <rest-argument>...))
      +
      +
    • +
    + +

    Implementation

    + +Here is an implementation using SYNTAX-RULES. + +

    +

    +;; Use your own standard let.
    +;; Or call a lambda.
    +;; (define-syntax standard-let
    +;;
    +;;   (syntax-rules ()
    +;;
    +;;     ((let ((var val) ...) body ...)
    +;;      ((lambda (var ...) body ...) val ...))))
    +
    +(define-syntax let
    +
    +  (syntax-rules ()
    +
    +    ;; No bindings: use standard-let.
    +    ((let () body ...)
    +     (standard-let () body ...))
    +    ;; Or call a lambda.
    +    ;; ((lambda () body ...))
    +
    +    ;; All standard bindings: use standard-let.
    +    ((let ((var val) ...) body ...)
    +     (standard-let ((var val) ...) body ...))
    +    ;; Or call a lambda.
    +    ;; ((lambda (var ...) body ...) val ...)
    +
    +    ;; One standard binding: loop.
    +    ;; The all-standard-bindings clause didn't match,
    +    ;; so there must be a rest binding.
    +    ((let ((var val) . bindings) body ...)
    +     (let-loop #f bindings (var) (val) (body ...)))
    +
    +    ;; Signature-style name: loop.
    +    ((let (name binding ...) body ...)
    +     (let-loop name (binding ...) () () (body ...)))
    +
    +    ;; defun-style name: loop.
    +    ((let name bindings body ...)
    +     (let-loop name bindings () () (body ...)))))
    +
    +(define-syntax let-loop
    +
    +  (syntax-rules ()
    +
    +    ;; Standard binding: destructure and loop.
    +    ((let-loop name ((var0 val0) binding ...) (var ...     ) (val ...     ) body)
    +     (let-loop name (            binding ...) (var ... var0) (val ... val0) body))
    +
    +    ;; Rest binding, no name: use standard-let, listing the rest values.
    +    ;; Because of let's first clause, there is no "no bindings, no name" clause.
    +    ((let-loop #f (rest-var rest-val ...) (var ...) (val ...) body)
    +     (standard-let ((var val) ... (rest-var (list rest-val ...))) . body))
    +    ;; Or call a lambda with a rest parameter on all values.
    +    ;; ((lambda (var ... . rest-var) . body) val ... rest-val ...))
    +    ;; Or use one of several other reasonable alternatives.
    +
    +    ;; No bindings, name: call a letrec'ed lambda.
    +    ((let-loop name () (var ...) (val ...) body)
    +     ((letrec ((name (lambda (var ...) . body)))
    +        name)
    +      val ...))
    +
    +    ;; Rest binding, name: call a letrec'ed lambda.
    +    ((let-loop name (rest-var rest-val ...) (var ...) (val ...) body)
    +     ((letrec ((name (lambda (var ... . rest-var) . body)))
    +        name)
    +      val ... rest-val ...))))
    +
    + + +

    Copyright

    + +Copyright (C) Andy Gaynor (1999). All Rights Reserved. +

    This document and translations of it may be copied and furnished to +others, and derivative works that comment on or otherwise explain it or +assist in its implementation may be prepared, copied, published and distributed, +in whole or in part, without restriction of any kind, provided that the +above copyright notice and this paragraph are included on all such copies +and derivative works. However, this document itself may not be modified +in any way, such as by removing the copyright notice or references to the +Scheme Request For Implementation process or editors, except as needed +for the purpose of developing SRFIs in which case the procedures for copyrights +defined in the SRFI process must be followed, or as required to translate +it into languages other than English. +

    The limited permissions granted above are perpetual and will not be +revoked by the authors or their successors or assigns. +

    This document and the information contained herein is provided on an +"AS IS" basis and THE AUTHOR AND THE SRFI EDITORS DISCLAIM ALL WARRANTIES, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE +USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED +WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +


    +
    Editor: Mike Sperber
    + + + diff --git a/doc/srfi-std/srfi-54.html b/doc/srfi-std/srfi-54.html new file mode 100644 index 0000000000..da1c5a7284 --- /dev/null +++ b/doc/srfi-std/srfi-54.html @@ -0,0 +1,652 @@ + +SRFI 54: Formatting + +

    Title

    + +Formatting + +

    Author

    + +Joo ChurlSoo + +

    Status

    +This SRFI is currently in ``final'' status. To see an explanation of each status that a SRFI can hold, see here. +You can access the discussion via the archive of the mailing list. +

    + +

    Abstract

    + + +

    This SRFI introduces the CAT procedure that converts any object to a string. +It takes one object as the first argument and accepts a variable number of +optional arguments, unlike the procedure called FORMAT.

    + + +

    Rationale

    + + +

    It is difficult to gain a complete consensus for the design of a generic +formatting procedure that performs a variety of necessary functions in +addition to essential functions provided in C's PRINTF and Common lisp's +FORMAT. One of such ways would be to devise a free (and/or floating) sequence +method that easily handles optional arguments, in contrast to the conventional +fixed sequence method, in order to obtain a handy optional and functional +interface. With the concept of free sequencing, the CAT procedure is then +defined, not to process optional arguments with default values, but to process +default values with optional arguments.

    + +

    Issues

    + + +

    In converting a number to a string, it has not been tried to uniformly express +the exactness of a number. The CAT procedure makes it possible for the user +to prefix an exact sign to the resulting string as well as not to prefix it to +the resulting string as conventionally used when an exact number is made to +have a decimal point. An inexact sign is prefixed to the resulting string in +addition to an radix sign when an inexact number is converted to a +non-decimal.

    + +

    Specification

    + +
    +
    + +(CAT <object> [<exactness%>] [<radix%>] [<sign%>] [<precision%>] [<separator%>] + [<writer$>] [<pipe$>] [<take$>] + [<converter*>] [<width*>] [<char*>] [<port*>] [<string*>] ...) +
    +
    <suffix%>: effective only for the number type of <object>.
    + <suffix$>: effective for all types except the number type of <object>.
    + <suffix*>: effective for all types of <object>.
    +
    <object> is any Scheme object.
    +
    <exactness> is a symbol: exact or inexact.
    +
    <radix> is a symbol: binary, octal, decimal, or hexadecimal.
    + Each radix sign except decimal is prefixed to the resulting string.
    + The default value is decimal.
    +
    If <sign> is a symbol that takes the form of 'sign, and +<object> is a positive number without a positive sign, the +positive sign is prefixed to the resulting string.
    +
    <precision> is an inexact integer whose absolute value +specifies the number of decimal digits after a decimal point. If +<precision> is a non-negative integer, an exact sign is prefixed +to the resulting string as needed.
    +
    <separator> is a list whose first element is a character +serving as a separator and second element is a positive exact integer. +If the integer is n, the resulting string is separated in every +n-characters of the resulting string. When the integer is omitted, the +default value is 3.
    +
    <writer> is a procedure of two arguments; <object> and +a string port. It writes <object> to the string port. The default +value of <writer> is varied according to the type of +<object>. When <object> is a self-evaluating constant, it +becomes equivalent to DISPLAY procedure, otherwise, it becomes WRITE +procedure. If you want any objects to be displayed in your own way, you +have to define your own <writer>. Otherwise, they are displayed +simply in their evaluated forms.
    +
    <pipe> is a list which is composed of one or more procedures. +Each procedure takes at least one string argument and returns a string. +One procedure connects with another as a pipe.
    +
    <take> is a list whose elements are two exact integers; n and +m, and the absolute values of n and m are N and M, respectively. First, +the resulting string takes from the left n-characters, if it is +non-negative, or all the characters but N-characters, if negative. +Second, it takes from the right m-characters, if it is non-negative, or +all the characters but M-characters, if negative. Then, it concatenates +two set of characters taken. The second element can be omitted. If +omitted, the default value is 0.
    +
    <converter> is a pair whose car value is a predicate +procedure that checks whether <object> satisfies it, and cdr +value is a procedure that takes the <object> as an argument and +returns a string. When <object> satisfies the predicate +procedure, all optional arguments are ineffective except <width>, +<char>, <port>, and <string>.
    +
    <width> is an exact integer whose absolute value specifies +the width of the resulting string. When the resulting string has fewer +characters than the absolute value of <width>, it is placed +rightmost with the rest being padded with <char>s, if +<width> is positive, or it is placed leftmost with the rest being +padded with <char>s, if <width> is negative. On the other +hand, when the resulting string has more characters than the absolute +value of <width>, the <width> is ignored. The default value +is 0.
    +
    <char> is a padding character. The default value is #\space.
    +
    <port> is an output port or a boolean. If an output port is +specified, the resulting string and <string>s are output into +that port and simultaneously returned as a string. If <port> is +#t, the output port is current output port. If <port> is #f, the +output is only returned as a string. The default value is #f.
    +
    <string> is a string that is appended to the resulting string.
    +
    + +

    The order of all optional arguments does not matter. The CAT procedure +processes optional arguments in the following order; <exactness>, <radix>, +<precision>, <separator>, <sign> for the number type of <object>, or in the +following order; <writer>, <pipe>, <take> for all other types.

    + +

    Examples

    + +
    (cat 129.995 -10 2.) => "130.00    "
    +
    (cat 129.995 10 2.) => "    130.00"
    +
    (cat 129.985 10 2.) => "    129.98"
    +
    (cat 129.985001 10 2.) => "    129.99"
    +
    (cat 129.995 2. 'exact) => "#e130.00"
    +
    (cat 129 -2.) => "129.00"
    +
    (cat 129 2.) => "#e129.00"
    +
    (cat 129 10 2. #\0 'sign) => "#e+0129.00"
    +
    (cat 129 10 2. #\* 'sign) => "*#e+129.00"
    +
    (cat 1/3) => "1/3"
    +
    (cat 1/3 10 2.) => "    #e0.33"
    +
    (cat 1/3 10 -2.) => "      0.33"
    +
    (cat 129.995 10 '(#\, 2)) => " 1,29.99,5"
    +
    (cat 129995 10 '(#\,) 'sign) => "  +129,995"
    +
    (cat (cat 129.995 0.) '(0 -1)) => "130"
    +
    (cat 99.5 10 'sign 'octal) => "#i#o+307/2"
    +
    (cat 99.5 10 'sign 'octal 'exact) => "  #o+307/2"
    +
    (cat #x123 'octal 'sign) => "#o+443"
    +
    (cat #x123 -10 2. 'sign #\*) => "#e+291.00*"
    +
    (cat -1.2345e+15+1.2355e-15i 3.) => "-1.234e15+1.236e-15i"
    +
    (cat 1.2345e+15 10 3. 'sign) => " +1.234e15"
    +
    (cat "string" -10) => "string    "
    +
    (cat "string" 10 (list string-upcase)) => "    STRING"
    +
    (cat "string" 10 (list string-upcase) '(-2)) => "      RING"
    +
    (cat "string" 10 `(,string-titlecase) '(2 3)) => "     Sting"
    +
    (cat "string" `(,string-reverse ,string-upcase) => "GNIRTS"
    +
    (cat #\a 10) => "         a"
    +
    (cat 'symbol 10) => "    symbol"
    +
    (cat '#(#\a "str" s)) => "#(#\\a \"str\" s)"
    +
    (cat '(#\a "str" s)) => "(#\\a \"str\" s)"
    +
    (cat '(#\a "str" s) #t) => (#\a "str" s)"(#\\a \"str\" s)"
    +
    (cat '(#\a "str" s) (current-output-port)) => (#\a "str" s)"(#\\a \"str\" s)"
    +
    (cat 3 (cat 's) " " (cat "str" write)) => "3s \"str\""
    +
    (cat 3 #t (cat 's) " " (cat "str" write)) => 3s "str""3s \"str\""
    +
    (cat 3 #t (cat 's #t) " " (cat "str" write)) => s3s "str""3s \"str\""
    +
    +
    
    +(define-record-type :example
    +    (make-example num str)
    +    example?
    +    (num get-num set-num!)
    +    (str get-str set-str!))
    +(define ex (make-example 123 "string"))
    +(define (record->string object)
    +  (cat (get-num object) "-" (get-str object)))
    +(define (record-writer object string-port)
    +    (if (example? object)
    +	(begin (display (get-num object) string-port)
    +	       (display "-" string-port)
    +	       (display (get-str object) string-port))
    +	((or (and (or (string? object)
    +		      (char? object)
    +		      (boolean? object))
    +		  display)
    +	     write) object string-port)))
    +ex => '#{:example}
    +(cat ex) => "#{:example}"
    +(cat ex 20 record-writer) => "          123-string"
    +(cat ex 20 record-writer
    +     `(,(cut string-delete char-set:digit <>)
    +       ,string-upcase ,string-reverse)
    +     '(0 -1) #\-) => "--------------GNIRTS"
    +(cat "string" 20 record-writer
    +     (list string-upcase) '(2 3) #\-) => "---------------STING"
    +(cat 12 20 record-writer 3.) => "            #e12.000"
    +(cat ex 20 (cons example? record->string)) => "          123-string"
    +(cat ex 20 (cons example? record->string)
    +     `(,(cut string-delete char-set:digit <>)
    +       ,string-upcase ,string-reverse)
    +     '(0 -1) #\-) => "----------123-string"
    +(cat "string" 20 (cons example? record->string)
    +     (list string-upcase) '(2 3) #\-) => "---------------STING"
    +(cat 12 20 (cons example? record->string) -3.) => "              12.000"
    +
    +
    +
    + + +

    Implementation

    + +

    +

    The implementation below requires SRFI 6 +(Basic string ports) and SRFI 23 (Error +reporting mechanism). +

    + +
    
    +(define (cat object . rest)
    +  (let* ((str-rest (part string? rest))
    +	 (str-list (car str-rest))
    +	 (rest-list (cdr str-rest)))
    +    (if (null? rest-list)
    +	(apply string-append
    +	       (cond
    +		((number? object) (number->string object))
    +		((string? object) object)
    +		((char? object) (string object))
    +		((boolean? object) (if object "#t" "#f"))
    +		((symbol? object) (symbol->string object))
    +		(else
    +		 (get-output-string
    +		  (let ((str-port (open-output-string)))
    +		    (write object str-port)
    +		    str-port))))
    +	       str-list)
    +	(alet-cat* rest-list
    +	  ((width 0 (and (integer? width) (exact? width)))
    +	   (port #f (or (boolean? port) (output-port? port))
    +		 (if (eq? port #t) (current-output-port) port))
    +	   (char #\space (char? char))
    +	   (converter #f (and (pair? converter)
    +			      (procedure? (car converter))
    +			      (procedure? (cdr converter))))
    +	   (precision #f (and (integer? precision)
    +			      (inexact? precision)))
    +	   (sign #f (eq? 'sign sign))
    +	   (radix 'decimal
    +		  (memq radix '(decimal octal binary hexadecimal)))
    +	   (exactness #f (memq exactness '(exact inexact)))
    +	   (separator #f (and (list? separator)
    +			      (< 0 (length separator) 3)
    +			      (char? (car separator))
    +			      (or (null? (cdr separator))
    +				  (let ((n (cadr separator)))
    +				    (and (integer? n) (exact? n)
    +					 (< 0 n))))))
    +	   (writer #f (procedure? writer))
    +	   (pipe #f (and (list? pipe)
    +			 (not (null? pipe))
    +			 (every? procedure? pipe)))
    +	   (take #f (and (list? take)
    +			 (< 0 (length take) 3)
    +			 (every? (lambda (x)
    +				   (and (integer? x) (exact? x)))
    +				 take))))
    +	  (let* ((str
    +		  (cond
    +		   ((and converter
    +			 ((car converter) object))
    +		    (let* ((str ((cdr converter) object))
    +			   (pad (- (abs width) (string-length str))))
    +		      (cond
    +		       ((<= pad 0) str)
    +		       ((< 0 width) (string-append (make-string pad char) str))
    +		       (else (string-append str (make-string pad char))))))
    +		   ((number? object)
    +		    (and (not (eq? radix 'decimal)) precision
    +			 (error "cat: non-decimal cannot have a decimal point"))
    +		    (and precision (< precision 0) (eq? exactness 'exact)
    +			 (error "cat: exact number cannot have a decimal point without exact sign"))
    +		    (let* ((exact-sign (and precision
    +					    (<= 0 precision)
    +					    (or (eq? exactness 'exact)
    +						(and (exact? object)
    +						     (not (eq? exactness
    +							       'inexact))))
    +					    "#e"))
    +			   (inexact-sign (and (not (eq? radix 'decimal))
    +					      (or (and (inexact? object)
    +						       (not (eq? exactness
    +								 'exact)))
    +						  (eq? exactness 'inexact))
    +					      "#i"))
    +			   (radix-sign (cdr (assq radix
    +						  '((decimal . #f)
    +						    (octal . "#o")
    +						    (binary . "#b")
    +						    (hexadecimal . "#x")))))
    +			   (plus-sign (and sign (< 0 (real-part object)) "+"))
    +			   (exactness-sign (or exact-sign inexact-sign))
    +			   (str
    +			    (if precision
    +				(let ((precision (inexact->exact
    +						  (abs precision)))
    +				      (imag (imag-part object)))
    +				  (if (= 0 imag)
    +				      (e-mold object precision)
    +				      (string-append
    +				       (e-mold (real-part object) precision)
    +				       (if (< 0 imag) "+" "")
    +				       (e-mold imag precision)
    +				       "i")))
    +				(number->string
    +				 (cond
    +				  (inexact-sign (inexact->exact object))
    +				  (exactness
    +				   (if (eq? exactness 'exact)
    +				       (inexact->exact object)
    +				       (exact->inexact object)))
    +				  (else object))
    +				 (cdr (assq radix '((decimal . 10)
    +						    (octal . 8)
    +						    (binary . 2)
    +						    (hexadecimal . 16)))))))
    +			   (str
    +			    (if (and separator
    +				     (not (or (and (eq? radix 'decimal)
    +						   (str-index str #\e))
    +					      (str-index str #\i)
    +					      (str-index str #\/))))
    +				(let ((sep (string (car separator)))
    +				      (num (if (null? (cdr separator))
    +					       3 (cadr separator)))
    +				      (dot-index (str-index str #\.)))
    +				  (if dot-index
    +				      (string-append
    +				       (separate (substring str 0 dot-index)
    +						 sep num (if (< object 0)
    +							     'minus #t))
    +				       "."
    +				       (separate (substring
    +						  str (+ 1 dot-index)
    +						  (string-length str))
    +						 sep num #f))
    +				      (separate str sep num (if (< object 0)
    +								'minus #t))))
    +				str))
    +			   (pad (- (abs width)
    +				   (+ (string-length str)
    +				      (if exactness-sign 2 0)
    +				      (if radix-sign 2 0)
    +				      (if plus-sign 1 0))))
    +			   (pad (if (< 0 pad) pad 0)))
    +		      (if (< 0 width)
    +			  (if (char-numeric? char)
    +			      (if (< (real-part object) 0)
    +				  (string-append (or exactness-sign "")
    +						 (or radix-sign "")
    +						 "-"
    +						 (make-string pad char)
    +						 (substring str 1
    +							    (string-length
    +							     str)))
    +				  (string-append (or exactness-sign "")
    +						 (or radix-sign "")
    +						 (or plus-sign "")
    +						 (make-string pad char)
    +						 str))
    +			      (string-append (make-string pad char)
    +					     (or exactness-sign "")
    +					     (or radix-sign "")
    +					     (or plus-sign "")
    +					     str))
    +			  (string-append (or exactness-sign "")
    +					 (or radix-sign "")
    +					 (or plus-sign "")
    +					 str
    +					 (make-string pad char)))))
    +		   (else
    +		    (let* ((str (cond
    +				 (writer (get-output-string
    +					  (let ((str-port
    +						 (open-output-string)))
    +					    (writer object str-port)
    +					    str-port)))
    +				 ((string? object) object)
    +				 ((char? object) (string object))
    +				 ((boolean? object) (if object "#t" "#f"))
    +				 ((symbol? object) (symbol->string object))
    +				 (else (get-output-string
    +					(let ((str-port (open-output-string)))
    +					  (write object str-port)
    +					  str-port)))))
    +			   (str (if pipe
    +				    (let loop ((str ((car pipe) str))
    +					       (fns (cdr pipe)))
    +				      (if (null? fns)
    +					  str
    +					  (loop ((car fns) str)
    +						(cdr fns))))
    +				    str))
    +			   (str
    +			    (if take
    +				(let ((left (car take))
    +				      (right (if (null? (cdr take))
    +						 0 (cadr take)))
    +				      (len (string-length str)))
    +				  (define (substr str beg end)
    +				    (let ((end (cond
    +						((< end 0) 0)
    +						((< len end) len)
    +						(else end)))
    +					  (beg (cond
    +						((< beg 0) 0)
    +						((< len beg) len)
    +						(else beg))))
    +				      (if (and (= beg 0) (= end len))
    +					  str
    +					  (substring str beg end))))
    +				  (string-append
    +				   (if (< left 0)
    +				       (substr str (abs left) len)
    +				       (substr str 0 left))
    +				   (if (< right 0)
    +				       (substr str 0 (+ len right))
    +				       (substr str (- len right) len))))
    +				str))
    +			   (pad (- (abs width) (string-length str))))
    +		      (cond
    +		       ((<= pad 0) str)
    +		       ((< 0 width) (string-append (make-string pad char) str))
    +		       (else (string-append str (make-string pad char))))))))
    +		 (str (apply string-append str str-list)))
    +	    (and port (display str port))
    +	    str)))))
    +
    +(define-syntax alet-cat*		; borrowed from SRFI-86
    +  (syntax-rules ()
    +    ((alet-cat* z (a . e) bd ...)
    +     (let ((y z))
    +       (%alet-cat* y (a . e) bd ...)))))
    +
    +(define-syntax %alet-cat*		; borrowed from SRFI-86
    +  (syntax-rules ()
    +    ((%alet-cat* z ((n d t ...)) bd ...)
    +     (let ((n (if (null? z)
    +		  d
    +		  (if (null? (cdr z))
    +		      (wow-cat-end z n t ...)
    +		      (error "cat: too many arguments" (cdr z))))))
    +       bd ...))
    +    ((%alet-cat* z ((n d t ...) . e) bd ...)
    +     (let ((n (if (null? z)
    +		  d
    +		  (wow-cat! z n d t ...))))
    +       (%alet-cat* z e bd ...)))
    +    ((%alet-cat* z e bd ...)
    +     (let ((e z)) bd ...))))
    +
    +(define-syntax wow-cat!			; borrowed from SRFI-86
    +  (syntax-rules ()
    +    ((wow-cat! z n d)
    +     (let ((n (car z)))
    +       (set! z (cdr z))
    +       n))
    +    ((wow-cat! z n d t)
    +     (let ((n (car z)))
    +       (if t
    +	   (begin (set! z (cdr z)) n)
    +	   (let lp ((head (list n)) (tail (cdr z)))
    +	     (if (null? tail)
    +		 d
    +		 (let ((n (car tail)))
    +		   (if t
    +		       (begin (set! z (append (reverse head) (cdr tail))) n)
    +		       (lp (cons n head) (cdr tail)))))))))
    +    ((wow-cat! z n d t ts)
    +     (let ((n (car z)))
    +       (if t
    +	   (begin (set! z (cdr z)) ts)
    +	   (let lp ((head (list n)) (tail (cdr z)))
    +	     (if (null? tail)
    +		 d
    +		 (let ((n (car tail)))
    +		   (if t
    +		       (begin (set! z (append (reverse head) (cdr tail))) ts)
    +		       (lp (cons n head) (cdr tail)))))))))
    +    ((wow-cat! z n d t ts fs)
    +     (let ((n (car z)))
    +       (if t
    +	   (begin (set! z (cdr z)) ts)
    +	   (begin (set! z (cdr z)) fs))))))
    +
    +(define-syntax wow-cat-end		; borrowed from SRFI-86
    +  (syntax-rules ()
    +    ((wow-cat-end z n)
    +     (car z))
    +    ((wow-cat-end z n t)
    +     (let ((n (car z)))
    +       (if t n (error "cat: too many argument" z))))
    +    ((wow-cat-end z n t ts)
    +     (let ((n (car z)))
    +       (if t ts (error "cat: too many argument" z))))
    +    ((wow-cat-end z n t ts fs)
    +     (let ((n (car z)))
    +       (if t ts fs)))))
    +
    +(define (str-index str char)
    +  (let ((len (string-length str)))
    +    (let lp ((n 0))
    +      (and (< n len)
    +	   (if (char=? char (string-ref str n))
    +	       n
    +	       (lp (+ n 1)))))))
    +
    +(define (every? pred ls)
    +  (let lp ((ls ls))
    +    (or (null? ls)
    +	(and (pred (car ls))
    +	     (lp (cdr ls))))))
    +
    +(define (part pred ls)
    +  (let lp ((ls ls) (true '()) (false '()))
    +    (cond
    +     ((null? ls) (cons (reverse true) (reverse false)))
    +     ((pred (car ls)) (lp (cdr ls) (cons (car ls) true) false))
    +     (else (lp (cdr ls) true (cons (car ls) false))))))
    +
    +(define (e-mold num pre)
    +  (let* ((str (number->string (exact->inexact num)))
    +	 (e-index (str-index str #\e)))
    +    (if e-index
    +	(string-append (mold (substring str 0 e-index) pre)
    +		       (substring str e-index (string-length str)))
    +	(mold str pre))))
    +
    +(define (mold str pre)
    +  (let ((ind (str-index str #\.)))
    +    (if ind
    +	(let ((d-len (- (string-length str) (+ ind 1))))
    +	  (cond
    +	   ((= d-len pre) str)
    +	   ((< d-len pre) (string-append str (make-string (- pre d-len) #\0)))
    +	   ;;((char<? #\4 (string-ref str (+ 1 ind pre)))
    +	   ;;(let ((com (expt 10 pre)))
    +	   ;;  (number->string (/ (round (* (string->number str) com)) com))))
    +	   ((or (char<? #\5 (string-ref str (+ 1 ind pre)))
    +		(and (char=? #\5 (string-ref str (+ 1 ind pre)))
    +		     (or (< (+ 1 pre) d-len)
    +			 (memv (string-ref str (+ ind (if (= 0 pre) -1 pre)))
    +			       '(#\1 #\3 #\5 #\7 #\9)))))
    +	    (apply
    +	     string
    +	     (let* ((minus (char=? #\- (string-ref str 0)))
    +		    (str (substring str (if minus 1 0) (+ 1 ind pre)))
    +		    (char-list
    +		     (reverse
    +		      (let lp ((index (- (string-length str) 1))
    +			       (raise #t))
    +			(if (= -1 index)
    +			    (if raise '(#\1) '())
    +			    (let ((chr (string-ref str index)))
    +			      (if (char=? #\. chr)
    +				  (cons chr (lp (- index 1) raise))
    +				  (if raise
    +				      (if (char=? #\9 chr)
    +					  (cons #\0 (lp (- index 1) raise))
    +					  (cons (integer->char
    +						 (+ 1 (char->integer chr)))
    +						(lp (- index 1) #f)))
    +				      (cons chr (lp (- index 1) raise))))))))))
    +	       (if minus (cons #\- char-list) char-list))))
    +	   (else
    +	    (substring str 0 (+ 1 ind pre)))))
    +	(string-append str "." (make-string pre #\0)))))
    +
    +(define (separate str sep num opt)
    +  (let* ((len (string-length str))
    +	 (pos (if opt
    +		  (let ((pos (remainder (if (eq? opt 'minus) (- len 1) len)
    +					num)))
    +		    (if (= 0 pos) num pos))
    +		  num)))
    +    (apply string-append
    +	   (let loop ((ini 0)
    +		      (pos (if (eq? opt 'minus) (+ pos 1) pos)))
    +	     (if (< pos len)
    +		 (cons (substring str ini pos)
    +		       (cons sep (loop pos (+ pos num))))
    +		 (list (substring str ini len)))))))
    +
    +;;; eof
    +
    +
    + + +

    Acknowledgment

    + + +

    I owe much to those who incite me to make this SRFI better. And I +must thank Michael Sperber for his encouragement and guidance during +the draft period of SRFI 51, as the important part of this SRFI is +based on SRFI 51. Without him, neither SRFI 51 nor SRFI 54 would have +been finilized. Again, I greatly appreciate his kindness and effort. +Finally, I deeply apologize to Francisco Solsona for bringing disgrace +upon him against my will early in the draft period.

    + + +

    Copyright

    + +

    Copyright (C) Joo ChurlSoo (2004). All Rights Reserved.

    + +

    +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: +

    +

    +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. +

    +

    +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +

    + +
    +
    Editor: Francisco Solsona
    + + +Last modified: Sun Jan 28 13:40:19 MET 2007 + + + diff --git a/doc/srfi-std/srfi-57.html b/doc/srfi-std/srfi-57.html new file mode 100644 index 0000000000..e86c6be461 --- /dev/null +++ b/doc/srfi-std/srfi-57.html @@ -0,0 +1,1389 @@ + +SRFI 57: Records + + +

    Title

    +Records + +

    Author

    +André van Tonder + +

    Status

    + +This SRFI is currently in ``final'' status. To see an explanation of each +status that a SRFI can hold, see here. You can access +previous messages via the +archive of the mailing list. + +

    +

    + +

    Abstract

    + +We describe a syntax for defining record types. +A predicate, constructor, +and field accessors and modifiers may be specified for each record +type. +We also introduce a syntax for declaring record type schemes, representing +families of record types that share a set of field labels. +A polymorphic predicate and +polymorphic field accessors and modifiers may be specified for each +record type scheme. +A syntax is provided for constructing records by +field label, for in-place and for functional record +update, and for composing records. + + +

    Rationale

    + +We extend the existing SRFI-9 [1] with the following features, +each listed with a brief justification. Expanded rationales appear +in the specification section below. + +
      +
    • A syntax for constructing record values by field label. Such a + feature is common in the prior art [2]. Indeed, the ability to + address fields by labels instead of position is one of the main + reasons for introducing the concept of records in the first + place. + Taking advantage of this feature can ease coding and increase readability and + robustness of code. +
    • + A mechanism for defining and incrementally extending + polymorphic functions on families + of record types sharing a set of field labels. This automates + a common programming pattern which would otherwise be laborious to + code explicitly. +
    • Facilities for record composition, functional record update, + including polymorphic + update, and linear in-place update. + These facilitate functional style programming with records. +
    • A declaration syntax that allows shorter variations and + optional elements, to save labor and reduce namespace + pollution. +
    + + +

    Specification

    + +

    Declaration

    + +
     <command or definition>           
    +   -> <record type definition>          
    +   -> <record scheme definition>           ; addition to 7.1.6 in R5RS
    +
    + <record type definition> -> (define-record-type <type clause> 
    +                                                 <constructor clause> 
    +                                                 <predicate clause>                          
    +                                                 <field clause> ...)  
    +                          -> (define-record-type <type clause> 
    +                                                 <constructor clause>)  
    +                          -> (define-record-type <type clause>)                   
    +
    + <record scheme definition> -> (define-record-scheme <scheme clause> 
    +                                                     <deconstructor clause> 
    +                                                     <predicate clause>                          
    +                                                     <field clause> ...)  
    +                            -> (define-record-scheme <scheme clause> 
    +                                                     <deconstructor clause>)  
    +                            -> (define-record-scheme <scheme clause>)                   
    +
    + <type clause> -> <type name>                           
    +               -> (<type name> <scheme name> ...)  
    +
    + <scheme clause> -> <scheme name>                           
    +                 -> (<scheme name> <parent scheme name> ...)  
    +
    + <constructor clause> -> (<constructor name> <field label> ...)               
    +                      -> <constructor name> 
    +                      -> #f
    +
    + <deconstructor clause> -> (<deconstructor name> <field label> ...)               
    +                        -> <deconstructor name> 
    +                        -> #f
    +
    + <predicate clause> -> <predicate name>                 
    +                    -> #f
    +
    + <field clause> -> (<field label> <accessor clause> <modifier clause>) 
    +                -> (<field label> <accessor clause>)
    +                -> (<field label>)
    +            
    + <accessor clause> -> <accessor name>                 
    +                   -> #f
    +
    + <modifier clause> -> <modifier name>                 
    +                   -> #f             
    +
    + <field label> -> <identifier>
    + <... name>    -> <identifier>
    +
    +
    + +

    Record types

    + +An instance of define-record-type is equivalent to the following: + +
      +
    • A list of field labels is associated with the record type <type name>, + obtained by appending from left to right the lists of field labels + of any record + type schemes (see below) appearing in the <type clause>, + followed by the list of labels in the + <constructor clause>, followed by the labels + in order of appearance in the <field + clause>s. + Duplicates are removed from the resulting list according + to the semantics of delete-duplicates of SRFI-1. + Labels in the constructor clause must be + distinct. Labels in the field clauses must also be distinct. +

      + +

    • For each <scheme name> in <type clause>, the record type + <type name> is said to be an instance of, or to + conform to the corresponding + record type scheme <scheme name> and to all + parent type schemes (see below) of <scheme name>. + +

    • <type name> is bound to a macro, described below, that can be used to construct record + values by label. It may also be registered, as specified in a + future SRFI, for performing pattern matching on record values of + type <type name>. +

      +

    • If <constructor clause> is + of the form (<constructor name> <field label> ...), then + <constructor name> is bound to a procedure that takes as many arguments as + there are <field label>s following it + and returns a new <type name> record. + Fields whose labels are listed with <type name> have the corresponding + argument as their initial value. The initial values of all other fields are unspecified. + If <constructor clause> is of the form <constructor name>, + the procedure + <constructor name> takes as many arguments as there are field labels + associated with <type name>, in the default order defined above. +

      + + <constructor name> may be + registered, in a way to be described in a future SRFI, for performing a + positional pattern match of the fields <field label> ... + of record + values of type <type name> in the first case, + or of all fields + associated with <scheme name> in the default + order defined above in the second case. + +

    • <predicate name>, is bound to a predicate procedure + that returns #t when given a record value that has been constructed using + the macro <type name> or the procedure <constructor name>, + and #f for any other + value. Values on which <predicate name>, if applied, would return + #t, are said to be of type <type name>. +

      + +

    • Field labels inherited from a <type scheme> or + introduced in the <constructor clause> do not have to be + repeated in the + <field clause>s. + Where present, <field + clause>s may provide additional information on such fields, or may + declare additional fields. + +

      + Field labels may be reused as the name of accessors or modifiers (a practice known + as punning). +

      +

        +

      • Each <accessor name> is bound to + a procedure that takes a + value of type <type name>, + and returns the current value of the corresponding + field. It is an error to pass an accessor a value not of type + <type name>. +

      • Each <modifier name> is bound to + a procedure that takes a value of type <type name> + and a value which becomes the new value of the corresponding field. + It is an error to pass a modifier a first argument that is not of type + <type name>. + The return value of <modifier name> is unspecified. +

        +

    + +

    +Define-record-type is generative: each use creates a new record type that is distinct +from all existing types, including +other record types and Scheme's predefined types. This SRFI only +specifies the behaviour of define-record-type at +top-level. + + +

    Record type schemes

    + +An instance of define-record-scheme is equivalent to the following: + +
      +
    • A list of field labels is associated with the type scheme <scheme name>, + obtained by appending from left to right the lists of field labels + of any parent + type schemes appearing in the <scheme clause>, + followed by the list of labels in the + <deconstructor clause>, followed by the labels + in order of appearance in the <field clause>s. + Duplicates are removed from the resulting list according + to the semantics of delete-duplicates of SRFI-1. + Labels in the constructor clause must be + distinct. Labels in the field clauses must also be distinct. +

      + +

    • A record type scheme is called a parent scheme of + <scheme name> if it appears in the + <scheme clause>, or if it is a parent scheme of + one of the <parent scheme name>'s appearing in the + <scheme clause>. + The type scheme + <scheme name> is said to + extend its parent type schemes. It is an error to extend a type scheme + that has not yet been defined. + + +

    • <scheme name> may be bound to a macro or otherwise + registered, in a way to be + described in a future + SRFI, + for performing pattern matching on record + values conforming to <scheme name>. +

      +

    • If <deconstructor clause> is + of the form (<deconstructor name> <field label> ...), then + <deconstructor name> may be bound to a macro or otherwise + registered, in a way to be described in a future SRFI, for performing a + positional pattern match of the fields <field label> ... + on record + values conforming to <scheme name>. + If <deconstructor clause> is of the form <deconstructor name>, + the positional match will be on all fields + associated with <scheme name>, in the default order defined above. +

      + +

    • <predicate name>, is bound to a predicate procedure + that returns #t when given a record value of any record type conforming + to <scheme name>, + and #f for any other + value. +

      + +

    • Field labels inherited from a <parent type scheme> or + introduced in the <deconstructor clause> do not have to be + repeated in the + <field clause>s. + Where present, <field + clause>s may provide additional information on such fields, or may + declare additional fields. + +

      + Field labels may be reused as the name of accessors or modifiers (a practice known + as punning). +

      +

        +

      • Each <accessor name> is bound to + a procedure that takes a + value conforming to <scheme name>, + and returns the current value of the corresponding + field. It is an error to pass an accessor a value not conforming to + <scheme name>. +

      • Each <modifier name> is bound to + a procedure that takes a value conforming to <scheme name> + and a value which becomes the new value of the corresponding field. + It is an error to pass a modifier a first argument that does not conform to + <scheme name>. + The return value of <modifier name> is unspecified. +

        +

    + + + + + + + + +

    Examples

    + +

    A simple record:

    + +
      (define-record-type point (make-point x y) point?
    +    (x get-x set-x!)           
    +    (y get-y set-y!))                    
    +
    +  (define p (make-point 1 2))
    +  (get-y  p)                                 ==> 2
    +  (set-y! p 3))                             
    +  (get-y  p)                                 ==> 3                             
    +  (point? p)                                 ==> #t  
    +
    + + +

    Record type schemes:

    + +Let's declare a couple of record schemes. Record schemes do not have constructors. +They introduce polymorphic predicates and accessors. +
      (define-record-scheme <point #f <point? 
    +    (x <point.x)
    +    (y <point.y))
    +
    +  (define-record-scheme <color #f <color?
    +    (hue <color.hue))
    +
    + +We now declare concrete instances of the above schemes. +Constructors may be introduced. +Predicates and accessors for concrete record types, when declared, are monomorphic. + +
      (define-record-type (point <point) make-point point?
    +    (x point.x)
    +    (y point.y))
    +
    +  (define-record-type (color <color) make-color)
    +
    +  (define-record-type (color-point <color <point) 
    +                      (make-color-point x y hue) color-point?
    +    (info color-point.info))
    +
    +  (define cp (make-color-point 1 2 'blue))
    +
    +  (<point?          cp)            ==> #t 
    +  (<color?          cp)            ==> #t
    +  (<point.y         cp)            ==> 2
    +  (<color.hue       cp)            ==> blue
    +  (point?           cp)            ==> #f      
    +  (point.x          cp)            ==> error   
    +  (color-point?     cp)            ==> #t
    +  (color-point.info cp)            ==> <undefined>
    +
    + +

    Optional elements:

    + +Elements may be left out if not desired, as the following examples illustrate: +
      
    +  (define-record-type node (make-node left right)) 
    +  (define-record-type leaf (make-leaf value))       
    +
    +In these declarations, no predicates are bound. Also note that field labels listed in the +constructor do not have to be repeated in the field clause list unless +we want to bind getters or setters. + +
                  
    +  (define-record-type monday)               
    +  (define-record-type tuesday #f tuesday?)      
    +
    +Here monday has no declared constructor or predicate, while tuesday +has a predicate but no constructor. + +
      (define-record-type node make-node #f                                   
    +    (left  left)                        
    +    (right right))                     
    +
    +Here the constructor make-node has the default argument order and no predicate +is bound. Also note that field labels are +punned. + + + + + + + + +

    A note on repeated fields and redefinitions

    + +In the following example, two record type schemes define different accessors for the same field: + +
      (define-record-scheme foo #f #f (x foo-x))
    +  (define-record-scheme bar #f #f (x bar-x))
    +  (define-record-type (foo-bar foo bar))
    +
    + +Since any value fb of type foo-bar conforms to both +foo and bar, both foo-x and bar-x +can be applied to fb, returning the value of the x field. + +

    +In the following example, two declarations introduce +the same accessor: + +

      (define-record-scheme foo     #f #f (x foo-x))
    +  (define-record-type (bar foo) #f #f (x foo-x))
    +
    +As in any define-... form, later bindings replace earlier bindings. +After the second declaration is executed, +foo-x will be bound to the monomorphic accessor applicable only to values + of type bar, replacing its binding to the polymorphic accessor procedure +introduced in the foo declaration. + + + + +

    Labeled record expressions

    + +The following syntax allows one to construct a record value by labels. The result +is a record value of type <type name> with each field +<field label> populated with the value of the corresponding +<expression>. The order of evaluation of the expressions +<expression> ... is undefined. All the +<field label>s have to belong to the record type <type name>. +If this condition is not satisfied, an expansion time error must be signaled. The +runtime efficiency of a labeled record expression is required to be at least that of +the equivalent positional constructor. + +
       <expression> -> (<type name> (<field label> <expression>) ...)
    +
    + +

    +The order of evaluation of the expressions +<expression> ... is undefined. + + + +

    Rationale

    + +The traditional practice of instantiating record values with a positional constructor procedure +can lead to code that is hard to read and fragile under common operations such as +adding, removing, or rearranging field declarations. The ability to populate record values +by labels provides a more robust and readable alternative, especially useful when a record has +more than two or three fields, or if it inherits fields from a type scheme. +Field labels are checked for validity +and the macro may be compiled to a positional constructor at expansion time, +thus eliminating a large class of potential programmer errors at no cost in efficiency. + +

    Example

    + +
      (color-point (info 'hi) 
    +               (x 1) 
    +               (y 2))  
    +            
    +                 ==> (color-point (hue <undefined>) (x 1) (y 2) (info hi)) 
    +
    + +

    Record update

    + +The following syntax allows different forms of record update: + +
       <expression> -> (record-update  <record> <scheme name> (<field label> <expression>) ...)
    +                -> (record-update  <record> <type name>   (<field label> <expression>) ...)
    +                -> (record-update! <record> <type name>   (<field label> <expression>) ...)
    +                -> (record-update! <record> <scheme name> (<field label> <expression>) ...)
    +
    + +The first alternative is used for polymorphic functional record update. The expression +<record> must evaluate to a record value that conforms to +<scheme name>. +The result will be a new record value of the same type as +the original <record>, with the given fields updated. The original +record value is unaffected. All the +<field label>s have to belong to the record type scheme <scheme name>. +If this condition is not satisfied, an expansion time error must be signaled. +

    + +The second alternative is used for monomorphic functional record update. The expression +<record> must evaluate to a record value of type +<type name>. The result will be a new record value of type +<type name>, with the given fields updated. The original +record value is unaffected. All the +<field label>s have to belong to the record type <type name>. +If this condition is not satisfied, an expansion time error must be signaled. +

    + +The third and fourth alternatives are used for linear, in-place record update. The expression +<record> must evaluate to a record value of type +<type name> or conforming to scheme <scheme name> . The result will be the original record value + with the given fields +mutated in place. +Note that a useful value is returned. All the +<field label>s have to belong to the record type <type name> or scheme <scheme name>. +If this condition is not satisfied, an expansion time error must be signaled. + +

    +In these forms, the order of evaluation of the expressions +<expression> ... is undefined. + +

    Rationale

    + +A mechanism for functional update facilitates and encourages functional-style programming +with records. +Note that polymorphic record update is not reducible +to the other operations we have listed and therefore has to be +provided as a built-in primitive [2]. + + +

    The linear version +update! is provided especially for cases where the programmer +knows that no other references to a value exist to produce what is, observationally, a +pure-functional result. In these cases, an update +operation may be replaced by update! for efficiency. +See SRFI-1 for a good discussion of the rationale behind linear update procedures. +Note, however, that in contrast with the linear procedures in SRFI-1, update! here is required +to mutate the original record. + +

    Examples

    + +Monomorphic update: +
      (define p (point (x 1) (y 2)))
    +
    +  (record-update p point (x 7))      ==> (point (x 7) (y 2))
    +  p                                  ==> (point (x 1) (y 2))   - original unaffected
    +
    + +Polymorphic update: +
      (define cp (color-point (hue 'blue) (x 1) (y 2)))
    + 
    +  (record-update cp <point (x 7))    ==> (color-point (info <undefined>) (hue blue) (x 7) (y 2))
    +  cp                                 ==> (color-point (info <undefined>) (hue blue) (x 1) (y 2))
    +
    + +In-place update: +
      (record-update! cp <point (x 7)))  ==> (color-point (info <undefined>) (hue blue) (x 7) (y 2))
    +  cp                                 ==> (color-point (info <undefined>) (hue blue) (x 7) (y 2))
    +
    + + +

    Record composition

    + +The following syntax provides a shorthand for composing record values: + +
       <expression> -> (record-compose (<import name> <record>) 
    +                                   ...
    +                                   (<export-type name> (<field label> <expression>) ...))
    +
    +   <import name> -> <type name>
    +                 -> <scheme name>
    +
    +Here each expression <record> must evaluate to a record value of type +<type name> or conforming to type scheme <scheme name>. The expression +evaluates to a new record value of type <export-type name> +whose fields are +populated as follows: For each field label belonging to <import name> + that is also a field label of the type +<export-type name>, the corresponding field of <record> +is copied into the result. This is done for all imports from left to +right, dropping any repeated fields. The additional fields <field label> +are then populated with the value of the + corresponding <expression>, overwriting +any fields with the same labels already imported. Any remaining fields are undefined. +All the +<field label>s have to belong to the record type <export type name>. +If this condition is not satisfied, an expansion time error must be signaled. + +

    +The order of evaluation of the expressions <record> ... and +<expression> ... is undefined. All the +expressions <record> ... will be evaluated, even +if their values might not be used in +the result. + +

    Rationale

    + +Calculi for composing record values, such as the above scheme, +may be used, for example, as units are used in +PLT Scheme, or for writing what amounts to modules and functors in the sense of ML.

    +Monomorphic record update is a special case of record-compose. The latter +may be used to express more general updates polymorphic in the +argument but monomorphic in the result type. +

    + + +

    Examples

    + +Use record-compose for updates polymorphic in the argument but +monomorphic in the result type: +
      (define cp (make-color-point 1 2 'green))
    +
    +  (record-compose (<point cp) (point (x 8)))   ==> (point (x 8) (y 2))
    +
    + +A more general composition example: + +
      (define cp (make-color-point 1 2 'green))
    +  (define c  (make-color 'blue))
    +  
    +  (record-compose (<point cp)                ; polymorphic import - only fields x and y of cp taken
    +                  (color  c)                 ; monomorphic import
    +                  (color-point (x 8)         ; overrides imported field
    +                               (info 'hi)))                 
    +                                      
    +                                         ==> (color-point (info hi) (hue blue) (x 8) (y 2))
    +
    + +Small module-functor example: +
      
    +  (define-record-type monoid #f #f 
    +    (mult monoid.mult) 
    +    (one  monoid.one))
    +
    +  (define-record-type abelian-group #f #f 
    +    (add  group.add) 
    +    (zero group.zero)
    +    (sub  group.sub))
    +
    +  (define-record-type ring #f #f
    +    (mult ring.mult) 
    +    (one  ring.one)
    +    (add  ring.add) 
    +    (zero ring.zero)
    +    (sub  ring.sub))
    +
    +  (define integer-monoid (monoid (mult *) 
    +                                 (one  1)))
    +
    +  (define integer-group (abelian-group (add  +)
    +                                       (zero 0)
    +                                       (sub  -)))
    +
    +  (define (make-ring g m)          ; simple functor a la ML
    +    (record-compose (monoid m)
    +                    (abelian-group g)
    +                    (ring)))
    +
    +  (define integer-ring (make-ring integer-group 
    +                                  integer-monoid))
    +  
    +  ((ring.add integer-ring) 1
    +                           2)    ==> 3
    +
    + + + +

    Implementation

    + +

    +The reference implementation uses the macro mechanism of +R5RS. It assumes an existing implementation of SRFI-9, here denoted +srfi-9:define-record-type. It also contains a trivial use of +case-lambda from SRFI-16. + +

    +The reference implementation, though relatively portable as a set of +syntax-rules macros, is slow. For practical +implementations, it is recommended that a procedural macro system be +used. Such implementations are provided separately in the discussion +archives. Unless otherwise stated by the author(s), they are covered +by the same copyright agreement as this document. + + +

    +This version depends on define being treated as a binding +form by syntax-rules. This is true for recent versions of portable syntax-case as used in Chez Scheme. It is +also true for PLT, for Scheme48, and possibly others. It also assumes +that the implementation of SRFI-9 binds the type name passed to it, which is a +hygienically introduced internal identifier, +using define. + + +

    +The SRFI specification was designed with the constraint that +all record expressions containing field labels be translatable into positional +expressions at macro-expansion time. For example, labeled record expressions +and patterns should be just as efficient as positional constructors and +patterns. This is true for the reference implementation. + +

    +Only the names mentioned in the specification should be visible to +the user. Other +names should be hidden by a module system or naming convention. +

    +The last section contains a few examples and (non-exhaustive) tests. + + + +

    +

    Reference implementation

    + + +
    ;============================================================================================
    +; IMPLEMENTATION:
    +;
    +; Andre van Tonder, 2004.
    +;
    +;============================================================================================
    +
    +(define-syntax define-record-type    
    +  (syntax-rules ()
    +    ((define-record-type . body)
    +     (parse-declaration #f . body))))
    +
    +(define-syntax define-record-scheme    
    +  (syntax-rules ()
    +    ((define-record-scheme . body)
    +     (parse-declaration #t . body))))
    +
    +(define-syntax parse-declaration    
    +  (syntax-rules ()
    +    ((parse-declaration is-scheme? (name super ...) constructor-clause predicate field-clause ...)
    +     (build-record 0 constructor-clause (super ...) (field-clause ...) name predicate is-scheme?))
    +    ((parse-declaration is-scheme? (name super ...) constructor-clause)
    +     (parse-declaration is-scheme? (name super ...) constructor-clause #f))  
    +    ((parse-declaration is-scheme? (name super ...))
    +     (parse-declaration is-scheme? (name super ...) #f #f))
    +    ((parse-declaration is-scheme? name . rest)
    +     (parse-declaration is-scheme? (name) . rest))))
    +
    +(define-syntax record-update!
    +  (syntax-rules ()
    +    ((record-update! record name (label exp) ...)
    +     (meta
    +      `(let ((r record)) 
    +         ((meta ,(name ("setter") label)) r exp)
    +         ...
    +         r)))))
    +
    +(define-syntax record-update
    +  (syntax-rules ()
    +    ((record-update record name (label exp) ...)
    +     (name ("is-scheme?")
    +           (meta                                                         
    +            `(let ((new ((meta ,(name ("copier"))) record)))
    +               (record-update! new name (label exp) ...)))
    +           (record-compose (name record) (name (label exp) ...))))))    
    +           
    +(define-syntax record-compose
    +  (syntax-rules ()
    +    ((record-compose (export-name (label exp) ...))
    +     (export-name (label exp) ...))
    +    ((record-compose (import-name record) ... (export-name (label exp) ...))
    +     (help-compose 1 (import-name record) ... (export-name (label exp) ...)))))
    +
    +(define-syntax help-compose
    +  (syntax-rules ()
    +    ((help-compose 1 (import-name record) import ... (export-name (label exp) ...))
    +     (meta
    +      `(help-compose 2
    +                     (meta ,(intersection
    +                             (meta ,(export-name ("labels")))
    +                             (meta ,(remove-from (meta ,(import-name ("labels")))
    +                                                 (label ...)
    +                                                 if-free=))
    +                             if-free=))
    +                     (import-name record) 
    +                     import ...
    +                     (export-name (label exp) ...))))
    +    ((help-compose 2 (copy-label ...) (import-name record) import ... (export-name . bindings))
    +     (meta
    +      `(let ((r record))
    +         (record-compose import ...
    +           (export-name (copy-label ((meta ,(import-name ("getter") copy-label)) r))
    +                        ...
    +                        . bindings)))))))
    +
    +(define-syntax build-record
    +  (syntax-rules ()
    +   ((build-record 0 (constructor . pos-labels) . rest)              ; extract positional labels from constructor clause
    +    (build-record 1 (constructor . pos-labels) pos-labels . rest))  ; 
    +   ((build-record 0 constructor . rest)                             ; 
    +    (build-record 1 (constructor . #f) () . rest))                  ; 
    +   ((build-record 1 constructor-clause (pos-label ...) (super ...)  
    +                    ((label . accessors) ...) . rest)
    +    (meta 
    +     `(build-record 2
    +                    constructor-clause
    +                    (meta ,(union (meta ,(super ("labels")))        ; compute union of labels from supers,
    +                                  ...                               ; constructor clause and field clauses
    +                                  (pos-label ...) 
    +                                  (label ...)      
    +                                  top:if-free=))
    +                    ((label . accessors) ...)
    +                    (meta  ,(union (meta ,(super ("supers")))       ; compute transitive union of supers
    +                                   ...
    +                                   top:if-free=))
    +                    . rest)))
    +    ((build-record 2 (constructor . pos-labels) labels . rest)      ; insert default constructor labels if not given
    +     (syntax-if pos-labels
    +                (build-record 3 (constructor . pos-labels) labels . rest)
    +                (build-record 3 (constructor . labels)     labels . rest)))
    +    ((build-record 3 constructor-clause labels ((label . accessors) ...) . rest)
    +     (meta 
    +      `(build-record 4
    +                     (meta ,(remove-from labels                     ; separate the labels that do not appear in a
    +                                         (label ...)                ; field clause for next step
    +                                         top:if-free=))
    +                     ((label . accessors) ...) 
    +                     constructor-clause
    +                     labels
    +                     . rest)))
    +    ((build-record 4
    +                   (undeclared-label ...)
    +                   (field-clause ...)
    +                   (constructor . pos-labels)
    +                   labels
    +                   supers
    +                   name
    +                   predicate
    +                   is-scheme?)
    +     (meta
    +      `(build-record 5                                              ; generate identifiers for constructor, predicate
    +                     is-scheme?                                     ; getters and setters as needed 
    +                     name
    +                     supers
    +                     supers
    +                     labels 
    +                     (meta ,(to-identifier constructor))   
    +                     (meta ,(add-temporaries pos-labels))           ; needed for constructor below
    +                     (meta ,(to-identifier predicate))
    +                     (meta ,(augment-field field-clause)) 
    +                     ... 
    +                     (undeclared-label (meta ,(generate-identifier))
    +                                       (meta ,(generate-identifier)))
    +                     ...)))
    +    ((build-record 5
    +                   is-scheme?
    +                   name
    +                   (super ...)
    +                   supers
    +                   (label ...)
    +                   constructor  
    +                   ((pos-label pos-temp) ...) 
    +                   predicate
    +                   (field-label getter setter)
    +                   ...)  
    +     
    +     (begin
    +       (syntax-if is-scheme?
    +                  
    +                  (begin
    +                    (define-generic (predicate x) (lambda (x) #f))
    +                    (define-generic (getter x))
    +                    ...
    +                    (define-generic (setter x v))
    +                    ...
    +                    (define-generic (copy x)))
    +                  
    +                  (begin
    +                    (srfi-9:define-record-type internal-name
    +                                               (maker field-label ...)
    +                                               predicate
    +                                               (field-label getter setter) ...)  
    +       
    +                    (define constructor 
    +                      (lambda (pos-temp ...)
    +                        (populate 1 maker (field-label ...) (pos-label pos-temp) ...)))
    +       
    +                    (extend-predicates supers predicate)
    +                    (extend-accessors supers field-label predicate getter setter)
    +                    ...
    +       
    +                    (define (copy x)
    +                      (maker (getter x) ...))
    +                    (extend-copiers supers copy predicate)
    +   
    +                    (define-method (show (r predicate))
    +                      (list 'name
    +                            (list 'field-label (getter r)) 
    +                            ...))))    
    +       
    +       (define-syntax name
    +         (syntax-rules (field-label ...)
    +           ((name ("is-scheme?") sk fk)     (syntax-if is-scheme? sk fk))
    +           ((name ("predicate") k)          (syntax-apply k predicate))
    +           ((name ("supers") k)             (syntax-apply k (super ... name)))  
    +           ((name ("labels") k)             (syntax-apply k (label ...)))
    +           ((name ("pos-labels") k)         (syntax-apply k (pos-label ...)))
    +           ((name ("getter") field-label k) (syntax-apply k getter))   
    +           ...
    +           ((name ("getter") other k)       (syntax-apply k #f))
    +           ((name ("setter") field-label k) (syntax-apply k setter))  
    +           ...
    +           ((name ("setter") other k)       (syntax-apply k #f))
    +           ((name ("copier") k)             (syntax-apply k copy))
    +           ((name . bindings)               (populate 1 maker (field-label ...) . bindings))))))))
    +
    +
    +(define-syntax to-identifier
    +  (syntax-rules ()
    +    ((to-identifier #f k) (syntax-apply k generated-identifier))
    +    ((to-identifier id k) (syntax-apply k id))))
    +
    +(define-syntax augment-field 
    +  (syntax-rules ()
    +    ((augment-field (label) k)               (syntax-apply k (label generated-getter generated-setter)))
    +    ((augment-field (label getter) k)        (meta `(label (meta ,(to-identifier getter)) generated-setter) k))
    +    ((augment-field (label getter setter) k) (meta `(label (meta ,(to-identifier getter)) 
    +                                                           (meta ,(to-identifier setter))) k))))
    +
    +(define-syntax extend-predicates
    +  (syntax-rules ()
    +    ((extend-predicates (super ...) predicate)
    +     (begin
    +       (meta
    +        `(define-method (meta ,(super ("predicate")))
    +                        (predicate)
    +                        (x)
    +                        any?))   
    +       ...))))
    +
    +(define-syntax extend-copiers
    +  (syntax-rules ()
    +    ((extend-copiers (super ...) copy predicate)
    +     (begin
    +       (meta
    +        `(define-method (meta ,(super ("copier")))
    +                        (predicate)
    +                        (x)
    +                        copy))    
    +       ...))))
    +
    +(define-syntax extend-accessors
    +  (syntax-rules ()
    +    ((extend-accessors (super ...) label predicate selector modifier)
    +     (meta
    +      `(begin 
    +         (syntax-if (meta ,(super ("getter") label))
    +                    (define-method (meta ,(super ("getter") label))
    +                                   (predicate)
    +                                   (x)
    +                                   selector)
    +                    (begin))
    +         ...
    +         (syntax-if (meta ,(super ("setter") label))
    +                    (define-method (meta ,(super ("setter") label))
    +                                   (predicate any?)
    +                                   (x v)
    +                                   modifier)
    +                    (begin))
    +         ...)))))
    +
    +(define-syntax populate
    +  (syntax-rules ()
    +    ((populate 1 maker labels . bindings)
    +     (meta 
    +      `(populate 2 maker
    +                   (meta ,(order labels bindings ('<undefined>))))))
    +    ((populate 2 maker ((label exp) ...))
    +     (maker exp ...))))
    +
    +(define-syntax order
    +  (syntax-rules ()
    +    ((order (label ...) ((label* . binding) ...) default k)
    +     (meta
    +      `(if-empty? (meta ,(remove-from (label* ...) 
    +                                      (label ...) 
    +                                      if-free=))
    +                  (order "emit" (label ...) ((label* . binding) ...) default k)
    +                  (syntax-error "Illegal labels in" ((label* . binding) ...)
    +                                "Legal labels are" (label ...)))))
    +    ((order "emit" (label ...) bindings default k)
    +     (meta 
    +      `((label . (meta ,(syntax-lookup label 
    +                                       bindings 
    +                                       if-free= 
    +                                       default)))
    +        ...)
    +      k))))
    +
    +
    +;============================================================================================
    +; Simple generic functions:
    +
    +(define-syntax define-generic
    +  (syntax-rules ()
    +    ((define-generic (name arg ...))
    +     (define-generic (name arg ...)
    +       (lambda (arg ...) (error "Inapplicable method:" 'name
    +                                "Arguments:" (show arg) ... ))))
    +    ((define-generic (name arg ...) proc)
    +     (define name (make-generic (arg ...) proc)))))  
    +  
    +(define-syntax define-method
    +  (syntax-rules ()
    +    ((define-method (generic (arg pred?) ...) . body)
    +     (define-method generic (pred? ...) (arg ...) (lambda (arg ...) . body))) 
    +    ((define-method generic (pred? ...) (arg ...) procedure)
    +     (let ((next ((generic) 'get-proc))
    +           (proc procedure))
    +       (((generic) 'set-proc)
    +        (lambda (arg ...)
    +          (if (and (pred? arg) ...)
    +              (proc arg ...)
    +              (next arg ...))))))))
    +
    +(define-syntax make-generic
    +  (syntax-rules ()
    +    ((make-generic (arg arg+ ...) default-proc)
    +     (let ((proc default-proc))
    +       (case-lambda
    +         ((arg arg+ ...)
    +          (proc arg arg+ ...))
    +         (()
    +          (lambda (msg)
    +            (case msg
    +              ((get-proc) proc)
    +              ((set-proc) (lambda (new)
    +                            (set! proc new)))))))))))
    +
    +(define-generic (show x) 
    +  (lambda (x) x))
    +
    +(define (any? x) #t)
    +
    +
    +;============================================================================================
    +; Syntax utilities:
    +
    +(define-syntax syntax-error
    +  (syntax-rules ()))
    +
    +(define-syntax syntax-apply
    +  (syntax-rules ()
    +    ((syntax-apply (f . args) exp ...) 
    +     (f exp ... . args))))
    +
    +(define-syntax syntax-cons
    +  (syntax-rules ()
    +    ((syntax-cons x rest k) 
    +     (syntax-apply k (x . rest)))))
    +
    +(define-syntax syntax-cons-after
    +  (syntax-rules ()
    +    ((syntax-cons-after rest x k)
    +     (syntax-apply k (x . rest)))))
    +
    +(define-syntax if-empty?
    +  (syntax-rules ()
    +    ((if-empty? () sk fk)      sk)
    +    ((if-empty? (h . t) sk fk) fk)))
    +
    +(define-syntax add-temporaries   
    +  (syntax-rules () 
    +    ((add-temporaries lst k)                (add-temporaries lst () k))
    +    ((add-temporaries () lst-temps k)       (syntax-apply k lst-temps))
    +    ((add-temporaries (h . t) (done ...) k) (add-temporaries t (done ... (h temp)) k))))
    +
    +(define-syntax if-free=
    +  (syntax-rules ()
    +    ((if-free= x y kt kf)
    +      (let-syntax
    +          ((test (syntax-rules (x)
    +                   ((test x kt* kf*) kt*)
    +                   ((test z kt* kf*) kf*))))
    +        (test y kt kf)))))
    +
    +(define-syntax top:if-free=
    +  (syntax-rules ()
    +    ((top:if-free= x y kt kf)
    +     (begin
    +       (define-syntax if-free=:test
    +         (syntax-rules (x)
    +           ((if-free=:test x kt* kf*) kt*)
    +           ((if-free=:test z kt* kf*) kf*)))
    +       (if-free=:test y kt kf)))))
    +
    +(define-syntax meta
    +  (syntax-rules (meta quasiquote unquote)
    +    ((meta `(meta ,(function argument ...)) k)
    +     (meta `(argument ...) (syntax-apply-to function k)))
    +    ((meta `(a . b) k)
    +     (meta `a (descend-right b k)))
    +    ((meta `whatever k) (syntax-apply k whatever))
    +    ((meta `arg)
    +     (meta `arg (syntax-id)))))
    +
    +(define-syntax syntax-apply-to
    +  (syntax-rules ()
    +    ((syntax-apply-to (argument ...) function k)
    +     (function argument ... k))))
    +
    +(define-syntax descend-right
    +  (syntax-rules ()
    +    ((descend-right evaled b k)
    +     (meta `b (syntax-cons-after evaled k)))))
    +
    +(define-syntax syntax-id
    +  (syntax-rules ()
    +    ((syntax-id arg) arg))) 
    +
    +(define-syntax remove-duplicates
    +  (syntax-rules ()
    +    ((remove-duplicates lst compare? k)
    +     (remove-duplicates lst () compare? k))
    +    ((remove-duplicates () done compare? k)
    +     (syntax-apply k done))
    +    ((remove-duplicates (h . t) (d ...) compare? k)
    +     (if-member? h (d ...) compare? 
    +                 (remove-duplicates t (d ...) compare? k)
    +                 (remove-duplicates t (d ... h) compare? k)))))
    +
    +(define-syntax syntax-filter
    +  (syntax-rules ()
    +    ((syntax-filter () (if-p? arg ...) k)
    +     (syntax-apply k ()))
    +    ((syntax-filter (h . t) (if-p? arg ...) k)
    +     (if-p? h arg ...
    +            (syntax-filter t (if-p? arg ...) (syntax-cons-after h k))
    +            (syntax-filter t (if-p? arg ...) k)))))
    +
    +(define-syntax if-member?
    +  (syntax-rules ()
    +    ((if-member? x () compare? sk fk) 
    +     fk)
    +    ((if-member? x (h . t) compare? sk fk)
    +     (compare? x h
    +               sk
    +               (if-member? x t compare? sk fk)))))
    +
    +(define-syntax union
    +  (syntax-rules ()
    +    ((union (x ...) ... compare? k)
    +     (remove-duplicates (x ... ...) compare? k))))
    +
    +(define-syntax intersection
    +  (syntax-rules ()
    +    ((intersection list1 list2 compare? k)
    +     (syntax-filter list1 (if-member? list2 compare?) k))))
    +
    +(define-syntax remove-from
    +  (syntax-rules ()
    +    ((remove-from list1 list2 compare? k)
    +     (syntax-filter list1 (if-not-member? list2 compare?) k))))
    +
    +(define-syntax if-not-member?
    +  (syntax-rules ()
    +    ((if-not-member? x list compare? sk fk)
    +     (if-member? x list compare? fk sk))))
    +
    +(define-syntax generate-identifier
    +  (syntax-rules ()
    +    ((generate-identifier k) (syntax-apply k generated-identifier))))
    +
    +(define-syntax syntax-if
    +  (syntax-rules ()
    +    ((syntax-if #f sk fk)    fk)
    +    ((syntax-if other sk fk) sk)))
    +
    +(define-syntax syntax-lookup
    +  (syntax-rules ()
    +    ((syntax-lookup label () compare fail k)
    +     (syntax-apply k fail))
    +    ((syntax-lookup label ((label* . value) . bindings) compare fail k)
    +     (compare label label*
    +              (syntax-apply k value)
    +              (syntax-lookup label bindings compare fail k)))))
    +
    + + + +

    Tests and examples

    + +
    ;============================================================================================
    +; Examples:
    +
    +; A simple record declaration:
    +
    +(define-record-type point (make-point x y) point?
    +  (x point.x point.x-set!)
    +  (y point.y point.y-set!))
    +
    +(define p (make-point 1 2))
    +
    +(point? p)             ;==> #t
    +(point.y p)            ;==> 2
    +(point.y-set! p 7)
    +(point.y p)            ;==> 7
    +
    +; Simple record schemes.
    +; Record schemes don't have constructors.
    +; The predicates and accessors are polymorphic.
    +
    +(define-record-scheme <point #f <point? 
    +  (x <point.x)
    +  (y <point.y))
    +
    +(define-record-scheme <color #f <color?
    +  (hue <color.hue))
    +
    +; Concrete instances of the above schemes.
    +; Constructors may be declared.
    +; Predicates and accessors, when provided, are monomorphic.  
    +
    +(define-record-type (point <point) make-point point?
    +  (x point.x)
    +  (y point.y))
    +
    +(define-record-type (color <color) make-color)
    +
    +(define-record-type (color-point <color <point) (make-color-point x y hue) color-point?
    +  (extra color-point.extra))
    +
    +(define cp (make-color-point 1 2 'blue))
    +
    +(<point? cp)            ;==> #t         
    +(<color? cp)            ;==> #t
    +(color-point? cp)       ;==> #t
    +;(point.x cp)           ;==> error 
    +(<point.y cp)           ;==> 2
    +(<color.hue cp)         ;==> blue
    +(color-point.extra cp)  ;==> <undefined>
    +
    +; Constructing records by field labels:
    +
    +(define p (point (x 1) 
    +                 (y 2)))
    +(define cp (color-point (hue 'blue) 
    +                        (x 1) 
    +                        (y 2)))
    +
    +; Monomorphic functional update:
    +
    +(show
    + (record-update p point (x 7)))     ;==> (point (x 7) (y 2))
    +(show p)                            ;==> (point (x 1) (y 2))   - original unaffected
    +
    +; Polymorphic functional update:
    +
    +(show 
    + (record-update cp <point (x 7)))   ;==> (color-point (extra <undefined>) (hue blue) (x 7) (y 2))
    +(show cp)                           ;==> (color-point (extra <undefined>) (hue blue) (x 1) (y 2))
    +
    +; In-place update:
    +
    +(show 
    + (record-update! cp <point (x 7)))  ;==> color-point (extra <undefined>) (hue blue) (x 7) (y 2))
    +(show cp)                           ;==> color-point (extra <undefined>) (hue blue) (x 7) (y 2))
    + 
    +; Use record-compose for updates polymorphic in argument but monomorphic in result type:
    +
    +(show
    + (record-compose (<point cp) (point (x 8))))  ;==> (point (x 8) (y 2))
    +(show cp)                                     ;==> (color-point (extra <undefined>) (hue blue) (x 7) (y 2))
    +
    +; More general record composition example:
    +
    +(define cp (make-color-point 1 2 'green))
    +(define c  (make-color 'blue))
    + 
    +(show 
    + (record-compose (<point cp)                 ; polymorphic import - only fields x and y of cp taken
    +                 (color c)                   ; monomorphic import
    +                 (color-point (x 8)          ; override imported field
    +                              (extra 'hi))))                 
    +                                      
    +                                         ;==> (color-point (extra hi) (hue blue) (x 8) (y 2))
    +
    +; Small module-functor example:
    +  
    +(define-record-type monoid #f #f 
    +  (mult monoid.mult) 
    +  (one  monoid.one))
    +
    +(define-record-type abelian-group #f #f 
    +  (add  group.add) 
    +  (zero group.zero)
    +  (sub  group.sub))
    +
    +(define-record-type ring #f #f
    +  (mult ring.mult) 
    +  (one  ring.one)
    +  (add  ring.add) 
    +  (zero ring.zero)
    +  (sub  ring.sub))
    +
    +(define integer-monoid (monoid (mult *) 
    +                               (one  1)))
    +
    +(define integer-group (abelian-group (add  +)
    +                                     (zero 0)
    +                                     (sub  -)))
    +
    +(define (make-ring g m)          ; simple "functor"
    +  (record-compose (monoid m)
    +                  (abelian-group g)
    +                  (ring)))
    +
    +(define integer-ring (make-ring integer-group 
    +                                integer-monoid))
    +  
    +((ring.add integer-ring) 1 2)    ;==> 3
    +
    +; Example of tree data type
    +
    +(define-record-scheme <tree #f <tree?) 
    +
    +(define-record-type (node <tree) make-node node?
    +  (lhs node.lhs)
    +  (rhs node.rhs))
    +
    +(define-record-type (leaf <tree) make-leaf leaf?
    +  (val leaf.val))
    +
    +(define (tree->list t)
    +  (cond
    +    ((leaf? t) (leaf.val t))
    +    ((node? t) (cons (tree->list (node.lhs t))
    +                     (tree->list (node.rhs t))))))
    +
    +(define t 
    +  (make-node (make-node (make-leaf 1)
    +                        (make-leaf 2))
    +             (make-leaf 3)))
    +
    +(<tree? t)         ;==> #t
    +(tree->list t)     ;==> ((1 . 2) . 3)
    +
    + + +

    References

    + +
    [1] Richard Kelsey, Defining Record Types, SRFI-9: http://srfi.schemers.org/srfi-9/srfi-9.html
    +
    +[2] See e.g.
    +    Benjamin C. Pierce, Types and Programming Languages, MIT Press 2002, and references therein.
    +    Mitchell Wand, Type inference for record concatenation and multiple inheritance, 
    +                   Information and Computation, v.93 n.1, p.1-15, July 1991
    +    John Reppy, Jon Riecke, Simple objects for Standard ML,
    +                Proceedings of the ACM SIGPLAN '96 Conference on Programming Language Design and Implementation
    +
    +
    +
    + + +

    Copyright

    + +

    Copyright (C) André van Tonder (2004). All Rights Reserved.

    + +

    +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: +

    +

    +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. +

    +

    +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +

    + +
    + +
    Author: André van +Tonder
    +
    Editor: David Van Horn
    + +Last modified: Wed Dec 8 15:31:34 EST 2004 + diff --git a/doc/srfi-std/srfi-59.html b/doc/srfi-std/srfi-59.html new file mode 100644 index 0000000000..d278922edd --- /dev/null +++ b/doc/srfi-std/srfi-59.html @@ -0,0 +1,350 @@ + + + + SRFI 59: Vicinity + + + + +

    Title

    + +Vicinity + +

    Author

    + +Aubrey Jaffer + +

    Status

    + +This SRFI is currently in ``final'' status. To see an explanation of each +status that a SRFI can hold, see here. You can access +previous messages via the +archive of the mailing list. + +

    +

      +
    • Received: 2004/12/30
    • +
    • Draft: 2004/12/30 - 2005/02/27
    • +
    • Final: 2005/03/08
    • +
    + +

    Abstract

    + +A vicinity is a descriptor for a place in the file system. Vicinities +hide from the programmer the concepts of host, volume, directory, and +version. Vicinities express only the concept of a file environment +where a file name can be resolved to a file in a system independent +manner. +

    +All of these procedures are file-system dependent. Use of these +vicinity procedures can make programs file-system +independent. +

    + +

    Rationale

    + +Most computer languages expose the syntax of pathnames of the host +file-system when dealing with files. +But a great many programs require read access to data, text, or +library files they were installed with. +

    +Some programs use literal strings to locate accessory files, breaking +on installations with different destinations. +More savvy coders will construct pathnames from environment variables +or compile-time definitions. +

    +In most languages, programs intended for portability must condition +all manipulations of pathnames to the syntax and capabilities of the +host file-system. Inconsistent conditioning is a common cause of +porting failures. +

    +Common-Lisp attacks the general problem of naming files anywhere in +any file system. It has a six-component pathname datatype +to represent names in the most complicated file-system imaginable; +names in simpler file systems use fewer components. +

    +In this arrangement, portable file-handling programs must be capable +of handling pathnames with 6 components, and those employing fewer. +But which component will be used is not obvious. Is a +".txt" filename suffix a type or part of the +name? +

    +Vicinities attack a smaller problem, that of describing pathnames in 5 +predefined locations, and their sub-vicinities. Those predefined +locations cover the usual areas for ancillary and configuration files +used by Scheme implementations and programs. The +program-vicinity is particularly useful as it is the +directory where the currently loading file is located. This is +captured by redefining load to fluid-let a +top-level variable with its argument. +

    +The make-vicinity and pathname->vicinity +procedures provide means to create new base vicinities. Base +vicinities should generally be absolute pathnames. +

    +Vicinities need not be tied to individual files in a file system. The +files named could be members of a zip archive, as Java does. +Vicinities can even be used on flat file systems (which have +no directory structure) by having the vicinity express constraints on +the file name. On most systems a vicinity is a string. +

    +vicinity +procedures are supported by all implementations in +SLIB. +

    + +

    Specification

    + +

    +

    +
    Function: program-vicinity +
    +Returns the vicinity of the currently loading Scheme code. For an +interpreter this would be the directory containing source code. For a +compiled system (with multiple files) this would be the directory +where the object or executable files are. If no file is currently +loading, then the result is undefined. Warning: +program-vicinity can return incorrect values if your program +escapes back into a load continuation. +
    + + +

    +

    +
    Function: library-vicinity +
    +Returns the vicinity of the shared Scheme library. +
    + + +

    +

    +
    Function: implementation-vicinity +
    +Returns the vicinity of the underlying Scheme implementation. This +vicinity will likely contain startup code and messages and a compiler. +
    + + +

    +

    +
    Function: user-vicinity +
    +Returns the vicinity of the current directory of the user. On most +systems this is `""' (the empty string). +
    + + +

    +

    +
    Function: home-vicinity +
    +Returns the vicinity of the user's HOME directory, the directory + +which typically contains files which customize a computer environment +for a user. If scheme is running without a user (eg. a daemon) or if +this concept is meaningless for the platform, then home-vicinity +returns #f. +
    + + +

    +

    +
    Function: in-vicinity vicinity filename +
    +Returns a filename suitable for use by load, +open-input-file, open-output-file, etc. The +returned filename is filename in vicinity. +in-vicinity should allow filename to override +vicinity when filename is an absolute pathname +and vicinity is equal to the value of +(user-vicinity). The behavior of +in-vicinity when filename is absolute and +vicinity is not equal to the value of +(user-vicinity) is unspecified. For most systems +in-vicinity can be string-append.
    + + +

    +

    +
    Function: sub-vicinity vicinity name +
    +Returns the vicinity of vicinity restricted to name. This +is used for large systems where names of files in subsystems could +conflict. On systems with directory structure sub-vicinity will +return a pathname of the subdirectory name of +vicinity. +
    + +

    +

    +
    Function: make-vicinity dirpath +
    +Returns dirpath as a vicinity for use as first argument to +in-vicinity. +
    + + +

    +

    +
    Function: pathname->vicinity path +
    +Returns the vicinity containing path. + +
    +(pathname->vicinity "/usr/local/lib/scm/Link.scm")
    +                    => "/usr/local/lib/scm/"
    +
    + +
    + + +

    +

    +
    Function: vicinity:suffix? chr +
    +Returns the `#t' if chr is a vicinity suffix character; and +#f otherwise. Typical vicinity suffixes are `/', +`:', and `\', +
    + + +

    Implementation

    + +This code is taken from +slib/Template.scm +and +slib/require.scm +

    + +

    +;;@ (implementation-vicinity) should be defined to be the pathname of
    +;;; the directory where any auxillary files to your Scheme
    +;;; implementation reside.
    +(define (implementation-vicinity)
    +  (case (software-type)
    +    ((UNIX)	"/usr/local/src/scheme/")
    +    ((VMS)	"scheme$src:")
    +    ((MS-DOS)	"C:\\scheme\\")))
    +
    +;;@ (library-vicinity) should be defined to be the pathname of the
    +;;; directory where files of Scheme library functions reside.
    +(define library-vicinity
    +  (let ((library-path
    +	 (or
    +	  ;; Use this getenv if your implementation supports it.
    +	  (getenv "SCHEME_LIBRARY_PATH")
    +	  ;; Use this path if your scheme does not support GETENV
    +	  ;; or if SCHEME_LIBRARY_PATH is not set.
    +	  (case (software-type)
    +	    ((UNIX) "/usr/local/lib/slib/")
    +	    ((VMS) "lib$scheme:")
    +	    ((MS-DOS) "C:\\SLIB\\")
    +	    (else "")))))
    +    (lambda () library-path)))
    +
    +;;@ (home-vicinity) should return the vicinity of the user's HOME
    +;;; directory, the directory which typically contains files which
    +;;; customize a computer environment for a user.
    +(define (home-vicinity)
    +  (let ((home (getenv "HOME")))
    +    (and home
    +	 (case (software-type)
    +	   ((UNIX COHERENT MS-DOS)	;V7 unix has a / on HOME
    +	    (if (eqv? #\/ (string-ref home (+ -1 (string-length home))))
    +		home
    +		(string-append home "/")))
    +	   (else home)))))
    +;@
    +(define in-vicinity string-append)
    +;@
    +(define (user-vicinity)
    +  (case (software-type)
    +    ((VMS)	"[.]")
    +    (else	"")))
    +;@
    +(define vicinity:suffix?
    +  (let ((suffi
    +	 (case (software-type)
    +	   ((AMIGA)				'(#\: #\/))
    +	   ((MACOS THINKC)			'(#\:))
    +	   ((MS-DOS WINDOWS ATARIST OS/2)	'(#\\ #\/))
    +	   ((NOSVE)				'(#\: #\.))
    +	   ((UNIX COHERENT PLAN9)		'(#\/))
    +	   ((VMS)				'(#\: #\]))
    +	   (else
    +	    (slib:warn "require.scm" 'unknown 'software-type (software-type))
    +	    "/"))))
    +    (lambda (chr) (and (memv chr suffi) #t))))
    +;@
    +(define (pathname->vicinity pathname)
    +  (let loop ((i (- (string-length pathname) 1)))
    +    (cond ((negative? i) "")
    +	  ((vicinity:suffix? (string-ref pathname i))
    +	   (substring pathname 0 (+ i 1)))
    +	  (else (loop (- i 1))))))
    +(define (program-vicinity)
    +  (if *load-pathname*
    +      (pathname->vicinity *load-pathname*)
    +      (slib:error 'program-vicinity "called while not within load")))
    +;@
    +(define sub-vicinity
    +  (case (software-type)
    +    ((VMS) (lambda
    +	       (vic name)
    +	     (let ((l (string-length vic)))
    +	       (if (or (zero? (string-length vic))
    +		       (not (char=? #\] (string-ref vic (- l 1)))))
    +		   (string-append vic "[" name "]")
    +		   (string-append (substring vic 0 (- l 1))
    +				  "." name "]")))))
    +    (else (let ((*vicinity-suffix*
    +		 (case (software-type)
    +		   ((NOSVE) ".")
    +		   ((MACOS THINKC) ":")
    +		   ((MS-DOS WINDOWS ATARIST OS/2) "\\")
    +		   ((UNIX COHERENT PLAN9 AMIGA) "/"))))
    +	    (lambda (vic name)
    +	      (string-append vic name *vicinity-suffix*))))))
    +;@
    +(define (make-vicinity pathname) pathname)
    +
    +

    + +

    Copyright

    +

    Copyright (C) Aubrey Jaffer (2004). All Rights Reserved.

    + +

    +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: +

    +

    +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. +

    +

    +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +

    + + +
    +
    Editor: David Van Horn
    + + +Last modified: Thu Dec 30 23:48:06 EST 2004 + + + diff --git a/doc/srfi-std/srfi-6.html b/doc/srfi-std/srfi-6.html new file mode 100644 index 0000000000..890e205f86 --- /dev/null +++ b/doc/srfi-std/srfi-6.html @@ -0,0 +1,353 @@ + + + + SRFI 6: Basic String Ports + + + + +

    Title

    + +SRFI-6: Basic String Ports + +

    Author

    + +William D Clinger + +

    Status

    + +This SRFI is currently in ``final'' status. To see an explanation of each status that a SRFI can hold, see here. +You can access the discussion via the archive of the mailing list. +

      +
    • Received: 1999/04/07 +
    • Draft: 1999/04/26-1999/06/24 +
    • Final: 1999/07/01 +
    + + +

    Abstract

    + +Scheme's i/o primitives are extended by adding three new procedures +that +
      +
    • create an input port from a string, +
    • create an output port whose contents are accumulated in Scheme's + working memory instead of an external file, and +
    • extract the accumulated contents of an in-memory output port + and return them in the form of a string. +
    + +

    Issues

    + +None. + +

    Rationale

    + +Scheme's procedures for performing input and output from and to ports +become more useful when extended by string ports. The interface +described here has been in use since 1986 or before, and is currently +supported by several of the major implementations. + +

    Specification

    + +This specification is taken from the MacScheme Reference Manual. + +
    +
    +(OPEN-INPUT-STRING string)                            ;procedure
    +
    + Takes a string and returns an input port that delivers characters + from the string. The port can be closed by + CLOSE-INPUT-PORT, though its storage will be + reclaimed by the garbage collector if it becomes inaccessible. +
    +        (define p
    +          (open-input-string "(a . (b . c . ())) 34"))
    +
    +        (input-port? p)                 -->  #t
    +        (read p)                        -->  (a b c)
    +        (read p)                        -->  34
    +        (eof-object? (peek-char p))     -->  #t
    +
    + +
    +(OPEN-OUTPUT-STRING)                                  ;procedure
    +
    + Returns an output port that will accumulate characters for + retrieval by GET-OUTPUT-STRING. The port can be + closed by the procedure CLOSE-OUTPUT-PORT, though + its storage will be reclaimed by the garbage collector if it + becomes inaccessible. +
    +        (let ((q (open-output-string))
    +              (x '(a b c)))
    +          (write (car x) q)
    +          (write (cdr x) q)
    +          (get-output-string q))        -->  "a(b c)"
    +
    + +
    +(GET-OUTPUT-STRING output-port)                       ;procedure
    +
    + Given an output port created by OPEN-OUTPUT-STRING, + returns a string consisting of the characters that have been + output to the port so far. +
    + + +

    Implementation

    + +What follows is just an outline of how these procedures might be +implemented, because a real implementation would also have to +redefine READ, WRITE, and so forth +to use PEEK-CHAR, READ-CHAR, and +WRITE-CHAR as redefined below. + +

    +Since the code for READ and WRITE +would be identical to code that already exists in any +implementation, however, it should not be necessary for this +SRFI to include that code within this SRFI. Including it +would only detract from the readability of this implementation. + +

    +

    +; This implementation is not IEEE- or R5RS-compliant,
    +; for the following reasons:
    +;
    +; This implementation does not redefine procedures
    +; like READ, WRITE, DISPLAY, and NEWLINE to ensure
    +; that they use the redefined PEEK-CHAR, READ-CHAR,
    +; WRITE-CHAR, and so forth.  That should be easy
    +; for an implementor to do, however.
    +;
    +; This implementation obtains an end-of-file object
    +; by reading a Unix-specific file, /dev/null.
    +
    +(define open-input-string 0)  ; assigned below
    +(define open-output-string 0) ; assigned below
    +(define get-output-string 0)  ; assigned below
    +
    +; We have to remember the original procedures before
    +; we can define new ones.
    +
    +(define ur-vector? vector?)
    +(define ur-vector-length vector-length)
    +(define ur-vector-ref vector-ref)
    +(define ur-vector-set! vector-set!)
    +(define ur-input-port? input-port?)
    +(define ur-output-port? output-port?)
    +(define ur-close-input-port close-input-port)
    +(define ur-close-output-port close-output-port)
    +(define ur-peek-char peek-char)
    +(define ur-read-char read-char)
    +(define ur-write-char write-char)
    +
    +; IEEE/ANSI Scheme insists that we define any global
    +; variables that we are going to assign.  R5RS Scheme
    +; apparently does not require this.
    +
    +(define vector? vector?)
    +(define vector-length vector-length)
    +(define vector-ref vector-ref)
    +(define vector-set! vector-set!)
    +(define input-port? input-port?)
    +(define output-port? output-port?)
    +(define close-input-port close-input-port)
    +(define close-output-port close-output-port)
    +(define peek-char peek-char)
    +(define read-char read-char)
    +(define write-char write-char)
    +
    +(let ((ur-vector? ur-vector?)
    +      (ur-vector-length ur-vector-length)
    +      (ur-vector-ref ur-vector-ref)
    +      (ur-vector-set! ur-vector-set!)
    +      (ur-input-port? ur-input-port?)
    +      (ur-output-port? ur-output-port?)
    +      (ur-close-input-port ur-close-input-port)
    +      (ur-close-output-port ur-close-output-port)
    +      (ur-peek-char ur-peek-char)
    +      (ur-read-char ur-read-char)
    +      (ur-write-char ur-write-char)
    +      (eof (call-with-input-file "/dev/null" read-char))
    +      (input-string-tag (list 'input-string-tag))
    +      (output-string-tag (list 'output-string-tag)))
    +  
    +  (define (error)
    +    (display "You're not supposed to do that!")
    +    (newline)
    +    (if #f #f))
    +  
    +  (define (restrict f pred?)
    +    (lambda (x . rest)
    +      (if (pred? x)
    +          (apply f x rest)
    +          (error))))
    +  
    +  (define (my-vector? x)
    +    (and (ur-vector? x)
    +         (not (input-string? x))
    +         (not (output-string? x))))
    +  
    +  (define (input-string? x)
    +    (and (ur-vector? x)
    +         (positive? (ur-vector-length x))
    +         (eq? input-string-tag (ur-vector-ref x 0))))
    +  
    +  (define (output-string? x)
    +    (and (ur-vector? x)
    +         (positive? (ur-vector-length x))
    +         (eq? output-string-tag (ur-vector-ref x 0))))
    +  
    +  (define (selector pred? i)
    +    (lambda (x)
    +      (if (pred? x)
    +          (ur-vector-ref x i)
    +          (error))))
    +  
    +  (define (setter pred? i)
    +    (lambda (x y)
    +      (if (pred? x)
    +          (begin (ur-vector-set! x i y)
    +                 (if #f #f))
    +          (error))))
    +  
    +  (set! vector?       my-vector?)
    +  (set! vector-length (restrict ur-vector-length my-vector?))
    +  (set! vector-ref    (restrict ur-vector-ref  my-vector?))
    +  (set! vector-set!   (restrict ur-vector-set! my-vector?))
    +  
    +  (let ()
    +    
    +    ; The guts of the implementation begin here.
    +    
    +    (define (make-input-string s)
    +      (vector input-string-tag #t s (string-length s) 0))
    +    
    +    (define input-string:open?  (selector input-string? 1))
    +    (define input-string:open?! (setter   input-string? 1))
    +    (define input-string:string (selector input-string? 2))
    +    (define input-string:size   (selector input-string? 3))
    +    (define input-string:next   (selector input-string? 4))
    +    (define input-string:next!  (setter   input-string? 4))
    +    
    +    (define (make-output-string)
    +      (vector output-string-tag #t '()))
    +    
    +    (define output-string:open?     (selector output-string? 1))
    +    (define output-string:open?!    (setter   output-string? 1))
    +    (define output-string:contents  (selector output-string? 2))
    +    (define output-string:contents! (setter   output-string? 2))
    +    
    +    (set! open-input-string make-input-string)
    +    (set! open-output-string make-output-string)
    +    (set! get-output-string
    +          (lambda (x)
    +            (list->string (reverse (output-string:contents x)))))
    +    
    +    (set! input-port?
    +          (lambda (x)
    +            (or (ur-input-port? x)
    +                (input-string? x))))
    +    
    +    (set! output-port?
    +          (lambda (x)
    +            (or (ur-output-port? x)
    +                (output-string? x))))
    +    
    +    (set! close-input-port
    +          (lambda (x)
    +            (if (input-string? x)
    +                (input-string:open?! x #f)
    +                (ur-close-input-port x))))
    +    
    +    (set! close-output-port
    +          (lambda (x)
    +            (if (output-string? x)
    +                (output-string:open?! x #f)
    +                (ur-close-output-port x))))
    +    
    +    (set! peek-char
    +          (lambda args
    +            (if (null? args)
    +                (ur-peek-char)
    +                (let ((x (car args)))
    +                  (if (input-string? x)
    +                      (let ((s (input-string:string x))
    +                            (i (input-string:next x))
    +                            (n (input-string:size x)))
    +                        (if (input-string:open? x)
    +                            (if (< i n)
    +                                (string-ref s i)
    +                                eof)
    +                            (error)))
    +                      (ur-peek-char x))))))
    +    
    +    (set! read-char
    +          (lambda args
    +            (if (null? args)
    +                (ur-read-char)
    +                (let ((x (car args)))
    +                  (if (input-string? x)
    +                      (let ((s (input-string:string x))
    +                            (i (input-string:next x))
    +                            (n (input-string:size x)))
    +                        (if (input-string:open? x)
    +                            (if (< i n)
    +                                (let ((c (string-ref s i)))
    +                                  (input-string:next! x (+ i 1))
    +                                  c)
    +                                eof)
    +                            (error)))
    +                      (ur-read-char x))))))
    +    
    +    (set! write-char
    +          (lambda (c . rest)
    +            (if (null? rest)
    +                (ur-write-char c)
    +                (let ((x (car rest)))
    +                  (if (output-string? x)
    +                      (if (output-string:open? x)
    +                          (output-string:contents!
    +                           x
    +                           (cons c (output-string:contents x)))
    +                          (error))
    +                      (ur-write-char c x))))))
    +    
    +    (if #f #f)))
    +
    + +

    Copyright

    +Copyright (C) William D Clinger (1999). All Rights Reserved. +

    +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: +

    +

    +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. +

    +

    +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +

    + +
    +
    Editor: Mike Sperber
    + + +Last modified: Mon Feb 24 11:54:01 MET 2003 + + + diff --git a/doc/srfi-std/srfi-60.html b/doc/srfi-std/srfi-60.html new file mode 100644 index 0000000000..f7516c397d --- /dev/null +++ b/doc/srfi-std/srfi-60.html @@ -0,0 +1,884 @@ + + + + SRFI 60: Integers as Bits + + + + +

    Title

    + +SRFI 60: Integers as Bits + +

    Author

    + +Aubrey Jaffer + +

    Status

    + +This SRFI is currently in ``final'' status. To see an explanation of each +status that a SRFI can hold, see here. You can access +previous messages via the +archive of the mailing list. + +

    +

    + +

    Abstract

    + +Treating integers as two's-complement strings of bits is an arcane but +important domain of computer science. It is used for: + +
      +
    • hashing; + +
    • Galois-field[2] calculations of error-detecting and + error-correcting codes; + +
    • cryptography and ciphers; + +
    • pseudo-random number generation; + +
    • register-transfer-level modeling of digital logic designs; + +
    • Fast-Fourier transforms; + +
    • packing and unpacking numbers in persistant data structures; + +
    • space-filling curves with applications to dimension reduction and + sparse multi-dimensional database indexes; and + +
    • generating approximate seed values for root-finders and + transcendental function algorithms. + +
    +

    + +

    Rationale

    + +This proposal describes the +SLIB module +logical, +which has been used for those purposes listed above. +

    +The discussions of the withdrawn +SRFI-33: "Integer +Bitwise-operation Library" seemed to founder on consistency of +procedure names and arity; and on perceived competition with the +boolean arrays of SRFI-47. +

    +I have implemented both logical number operations and boolean arrays; +and have not been conflicted as to their application. I used boolean +arrays to construct very fast indexes for database tables having +millions of records. To avoid running out of RAM, creation of megabit +arrays should be explicit; so the boolean array procedures put their +results into a passed array. In contrast, these procedures are purely +functional. +

    + +

    Bits and Complements

    + +A bit-index in these descriptions is nonnegative with the least +significant bit at index 0. +A positive integer has a finite number of "1" bits. +A negative integer has a finite number of "0" bits. +

    +The reference implementation is written using only Scheme integer +operations. Thus the only exposure of the underlying representation +is the ranges of fixnums. +

    +The complement describes the representation of negative +integers. With one's-complement fixnums, the range of integers is +-(2n) to 2n, and there are two +possible representations of 0. With two's-complement fixnums, the +range of integers is -(2n+1) to +2n. +

    +Since we treat integers as having two's-complement negations, +the two's-complement of an integer is simply its negation. +The one's-complement of an integer is computed by lognot: + +

    +(define (lognot n) (- -1 n))
    +
    +

    + +

    Bitwise Operations and Integer Properties

    + +The logior, logxor, logand, +lognot, logtest, logbit? (logbitp), +ash, logcount, and integer-length +procedures are from Common-Lisp. Logior, logxor, +and logand have been extended to accept any arity. +Opportunities to use an n-ary version of logtest have +not been frequent enough to justify its extension. +

    +In the Bitwise Operations, rather than striving for +orthogonal completeness, I have concentrated on a nearly minimal set +of bitwise logical functions sufficient to support the uses listed +above. +

    +Although any two of logior, logxor, and +logand (in combination with lognot) are sufficient +to generate all the two-input logic functions, having these three +means that any nontrivial two-input logical function can be +synthesized using just one of these two-input primaries with zero or +one calls to lognot. +

    +bitwise-if is what SRFI-33 calls bitwise-merge. +

    +The SRFI-33 aliases: bitwise-ior, bitwise-xor, +bitwise-and, bitwise-not, bitwise-merge, +any-bits-set?, and bit-count are also provided. +

    +log2-binary-factors (alias first-set-bit) is a +useful function which is simple but non-obvious: + +

    +(define (log2-binary-factors n)
    +  (+ -1 (integer-length (logand n (- n)))))
    +
    + +

    Bit Within Word and Field of Bits

    + +The Bit Within Word and Field of Bits procedures +are used for modeling digital logic and accessing binary data +structures in software. +

    +I have changed to copy-bit-field argument order to be +consistent with the other Field of Bits procedures: the +start and end index arguments are last. +This makes them analogous to the argument order to substring +and SRFI-47 arrays, which took their cue from substring. +

    +These start and end index arguments are not +compatible with SRFI-33's size and position +arguments (occurring first) in its bit-field procedures. +Both define copy-bit-field; the arguments and purposes being +incompatible. +

    +A procedure in slib/logical.scm, logical:rotate, rotated a +given number of low-order bits by a given number of bits. This +function was quite servicable, but I could not name it adequately. I +have replaced it with rotate-bit-field with the addition of a +start argument. This new function rotates a given field +(from positions start to end) within an integer; +leaving the rest unchanged. +

    +Another problematic name was logical:ones, which generated an +integer with the least significant k bits set. Calls to +bit-field could have replaced its uses . But the definition +was so short that I just replaced its uses with: + +

    +(lognot (ash -1 k))
    +
    +

    +The bit-reverse procedure was then the only one which took a +width argument. So I replaced it with +reverse-bit-field. +

    +The Lamination and Gray-code functions were +moved to +slib/phil-spc.scm +

    + +

    Bits as Booleans

    + +Bits as Booleans provides the procedures to convert between +integers and lists of booleans. There is no comparable facility in +SRFI-33. +

    + + +

    Specification

    + + + +

    Bitwise Operations

    + +

    +

    +
    Function: logand n1 ... +
    +
    Function: bitwise-and n1 ... +
    +Returns the integer which is the bit-wise AND of the integer +arguments. + + +

    +Example: + +

    +(number->string (logand #b1100 #b1010) 2)
    +    => "1000"
    +
    + +
    + +

    +

    +
    Function: logior n1 ... +
    +
    Function: bitwise-ior n1 ... +
    +Returns the integer which is the bit-wise OR of the integer arguments. + + +

    +Example: + +

    +(number->string (logior #b1100 #b1010) 2)
    +    => "1110"
    +
    + +
    + +

    +

    +
    Function: logxor n1 ... +
    +
    Function: bitwise-xor n1 ... +
    +Returns the integer which is the bit-wise XOR of the integer +arguments. + + +

    +Example: + +

    +(number->string (logxor #b1100 #b1010) 2)
    +    => "110"
    +
    + +
    + +

    +

    +
    Function: lognot n +
    +
    Function: bitwise-not n +
    +Returns the integer which is the one's-complement of the integer argument. + + +

    +Example: + +

    +(number->string (lognot #b10000000) 2)
    +    => "-10000001"
    +(number->string (lognot #b0) 2)
    +    => "-1"
    +
    + +
    + +

    +

    +
    Function: bitwise-if mask n0 n1 +
    +
    Function: bitwise-merge mask n0 n1 +
    +Returns an integer composed of some bits from integer n0 and some +from integer n1. A bit of the result is taken from n0 if the +corresponding bit of integer mask is 1 and from n1 if that bit +of mask is 0. +
    + + +

    +

    +
    Function: logtest j k +
    +
    Function: any-bits-set? j k +
    + +
    +(logtest j k) == (not (zero? (logand j k)))
    +
    +(logtest #b0100 #b1011) => #f
    +(logtest #b0100 #b0111) => #t
    +
    + +
    + + + +

    Integer Properties

    + +

    +

    +
    Function: logcount n +
    +
    Function: bit-count n +
    +Returns the number of bits in integer n. If integer is positive, +the 1-bits in its binary representation are counted. If negative, the +0-bits in its two's-complement binary representation are counted. If 0, +0 is returned. + + +

    +Example: + +

    +(logcount #b10101010)
    +    => 4
    +(logcount 0)
    +    => 0
    +(logcount -2)
    +    => 1
    +
    + +
    + +

    +

    +
    Function: integer-length n +
    +Returns the number of bits neccessary to represent n. + + +

    +Example: + +

    +(integer-length #b10101010)
    +    => 8
    +(integer-length 0)
    +    => 0
    +(integer-length #b1111)
    +    => 4
    +
    + +
    + +

    +

    +
    Function: log2-binary-factors n +
    +
    Function: first-set-bit n +
    +Returns the number of factors of two of integer n. This value +is also the bit-index of the least-significant `1' bit in +n. + + + +
    +(require 'printf)
    +(do ((idx 0 (+ 1 idx)))
    +      ((> idx 16))
    +    (printf "%s(%3d) ==> %-5d %s(%2d) ==> %-5d\n"
    +            'log2-binary-factors
    +            (- idx) (log2-binary-factors (- idx))
    +            'log2-binary-factors
    +            idx (log2-binary-factors idx)))
    +-|
    +log2-binary-factors(  0) ==> -1    log2-binary-factors( 0) ==> -1   
    +log2-binary-factors( -1) ==> 0     log2-binary-factors( 1) ==> 0    
    +log2-binary-factors( -2) ==> 1     log2-binary-factors( 2) ==> 1    
    +log2-binary-factors( -3) ==> 0     log2-binary-factors( 3) ==> 0    
    +log2-binary-factors( -4) ==> 2     log2-binary-factors( 4) ==> 2    
    +log2-binary-factors( -5) ==> 0     log2-binary-factors( 5) ==> 0    
    +log2-binary-factors( -6) ==> 1     log2-binary-factors( 6) ==> 1    
    +log2-binary-factors( -7) ==> 0     log2-binary-factors( 7) ==> 0    
    +log2-binary-factors( -8) ==> 3     log2-binary-factors( 8) ==> 3    
    +log2-binary-factors( -9) ==> 0     log2-binary-factors( 9) ==> 0    
    +log2-binary-factors(-10) ==> 1     log2-binary-factors(10) ==> 1    
    +log2-binary-factors(-11) ==> 0     log2-binary-factors(11) ==> 0    
    +log2-binary-factors(-12) ==> 2     log2-binary-factors(12) ==> 2    
    +log2-binary-factors(-13) ==> 0     log2-binary-factors(13) ==> 0    
    +log2-binary-factors(-14) ==> 1     log2-binary-factors(14) ==> 1    
    +log2-binary-factors(-15) ==> 0     log2-binary-factors(15) ==> 0    
    +log2-binary-factors(-16) ==> 4     log2-binary-factors(16) ==> 4    
    +
    + +
    + + + +

    Bit Within Word

    + +

    +

    +
    Function: logbit? index n +
    +
    Function: bit-set? index n +
    + +
    +(logbit? index n) == (logtest (expt 2 index) n)
    +
    +(logbit? 0 #b1101) => #t
    +(logbit? 1 #b1101) => #f
    +(logbit? 2 #b1101) => #t
    +(logbit? 3 #b1101) => #t
    +(logbit? 4 #b1101) => #f
    +
    + +
    + +

    +

    +
    Function: copy-bit index from bit +
    +Returns an integer the same as from except in the indexth bit, +which is 1 if bit is #t and 0 if bit is #f. + + +

    +Example: + +

    +(number->string (copy-bit 0 0 #t) 2)       => "1"
    +(number->string (copy-bit 2 0 #t) 2)       => "100"
    +(number->string (copy-bit 2 #b1111 #f) 2)  => "1011"
    +
    + +
    + + + +

    Field of Bits

    + +

    +

    +
    Function: bit-field n start end +
    +Returns the integer composed of the start (inclusive) through +end (exclusive) bits of n. The startth bit becomes +the 0-th bit in the result. + + +

    +Example: + +

    +(number->string (bit-field #b1101101010 0 4) 2)
    +    => "1010"
    +(number->string (bit-field #b1101101010 4 9) 2)
    +    => "10110"
    +
    + +
    + +

    +

    +
    Function: copy-bit-field to from start end +
    +Returns an integer the same as to except possibly in the +start (inclusive) through end (exclusive) bits, which are +the same as those of from. The 0-th bit of from becomes the +startth bit of the result. + + +

    +Example: + +

    +(number->string (copy-bit-field #b1101101010 0 0 4) 2)
    +    => "1101100000"
    +(number->string (copy-bit-field #b1101101010 -1 0 4) 2)
    +    => "1101101111"
    +(number->string (copy-bit-field #b110100100010000 -1 5 9) 2)
    +    => "110100111110000"
    +
    + +
    + +

    +

    +
    Function: ash n count +
    +
    Function: arithmetic-shift n count +
    +Returns an integer equivalent to +(inexact->exact (floor (* n (expt 2 count)))). + + +

    +Example: + +

    +(number->string (ash #b1 3) 2)
    +    => "1000"
    +(number->string (ash #b1010 -1) 2)
    +    => "101"
    +
    + +
    + +

    +

    +
    Function: rotate-bit-field n count start end +
    +Returns n with the bit-field from start to end +cyclically permuted by count bits towards high-order. + + +

    +Example: + +

    +(number->string (rotate-bit-field #b0100 3 0 4) 2)
    +    => "10"
    +(number->string (rotate-bit-field #b0100 -1 0 4) 2)
    +    => "10"
    +(number->string (rotate-bit-field #b110100100010000 -1 5 9) 2)
    +    => "110100010010000"
    +(number->string (rotate-bit-field #b110100100010000 1 5 9) 2)
    +    => "110100000110000"
    +
    + +
    + +

    +

    +
    Function: reverse-bit-field n start end +
    +Returns n with the order of bits start to end +reversed. + + + +
    +(number->string (reverse-bit-field #xa7 0 8) 16)
    +    => "e5"
    +
    + +
    + + + +

    Bits as Booleans

    + +

    +

    +
    Function: integer->list k len +
    +
    Function: integer->list k +
    +integer->list returns a list of len booleans corresponding +to each bit of the given integer. #t is coded for each 1; #f for 0. +The len argument defaults to (integer-length k). + + +

    +

    Function: list->integer list +
    +list->integer returns an integer formed from the booleans in the +list list, which must be a list of booleans. A 1 bit is coded for +each #t; a 0 bit for #f. + + +

    +integer->list and list->integer are inverses so far as +equal? is concerned. +

    + + +

    +

    +
    Function: booleans->integer bool1 ... +
    +Returns the integer coded by the bool1 ... arguments. +
    + + + +

    Implementation

    + +slib/logical.scm +implements the integers-as-bits procedures for R4RS or R5RS compliant +Scheme implementations. +

    +

    +;;;; "logical.scm", bit access and operations for integers for Scheme
    +;;; Copyright (C) 1991, 1993, 2001, 2003, 2005 Aubrey Jaffer
    +;
    +;Permission to copy this software, to modify it, to redistribute it,
    +;to distribute modified versions, and to use it for any purpose is
    +;granted, subject to the following restrictions and understandings.
    +;
    +;1.  Any copy made of this software must include this copyright notice
    +;in full.
    +;
    +;2.  I have made no warranty or representation that the operation of
    +;this software will be error-free, and I am under no obligation to
    +;provide any services, by way of maintenance, update, or otherwise.
    +;
    +;3.  In conjunction with products arising from the use of this
    +;material, there shall be no use of my name in any advertising,
    +;promotional, or sales literature without prior written consent in
    +;each case.
    +
    +(define logical:boole-xor
    + '#(#(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)
    +    #(1 0 3 2 5 4 7 6 9 8 11 10 13 12 15 14)
    +    #(2 3 0 1 6 7 4 5 10 11 8 9 14 15 12 13)
    +    #(3 2 1 0 7 6 5 4 11 10 9 8 15 14 13 12)
    +    #(4 5 6 7 0 1 2 3 12 13 14 15 8 9 10 11)
    +    #(5 4 7 6 1 0 3 2 13 12 15 14 9 8 11 10)
    +    #(6 7 4 5 2 3 0 1 14 15 12 13 10 11 8 9)
    +    #(7 6 5 4 3 2 1 0 15 14 13 12 11 10 9 8)
    +    #(8 9 10 11 12 13 14 15 0 1 2 3 4 5 6 7)
    +    #(9 8 11 10 13 12 15 14 1 0 3 2 5 4 7 6)
    +    #(10 11 8 9 14 15 12 13 2 3 0 1 6 7 4 5)
    +    #(11 10 9 8 15 14 13 12 3 2 1 0 7 6 5 4)
    +    #(12 13 14 15 8 9 10 11 4 5 6 7 0 1 2 3)
    +    #(13 12 15 14 9 8 11 10 5 4 7 6 1 0 3 2)
    +    #(14 15 12 13 10 11 8 9 6 7 4 5 2 3 0 1)
    +    #(15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0)))
    +
    +(define logical:boole-and
    + '#(#(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)
    +    #(0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1)
    +    #(0 0 2 2 0 0 2 2 0 0 2 2 0 0 2 2)
    +    #(0 1 2 3 0 1 2 3 0 1 2 3 0 1 2 3)
    +    #(0 0 0 0 4 4 4 4 0 0 0 0 4 4 4 4)
    +    #(0 1 0 1 4 5 4 5 0 1 0 1 4 5 4 5)
    +    #(0 0 2 2 4 4 6 6 0 0 2 2 4 4 6 6)
    +    #(0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7)
    +    #(0 0 0 0 0 0 0 0 8 8 8 8 8 8 8 8)
    +    #(0 1 0 1 0 1 0 1 8 9 8 9 8 9 8 9)
    +    #(0 0 2 2 0 0 2 2 8 8 10 10 8 8 10 10)
    +    #(0 1 2 3 0 1 2 3 8 9 10 11 8 9 10 11)
    +    #(0 0 0 0 4 4 4 4 8 8 8 8 12 12 12 12)
    +    #(0 1 0 1 4 5 4 5 8 9 8 9 12 13 12 13)
    +    #(0 0 2 2 4 4 6 6 8 8 10 10 12 12 14 14)
    +    #(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)))
    +
    +(define (logical:ash-4 x)
    +  (if (negative? x)
    +      (+ -1 (quotient (+ 1 x) 16))
    +      (quotient x 16)))
    +
    +(define (logical:reduce op4 ident)
    +  (lambda args
    +    (do ((res ident (op4 res (car rgs) 1 0))
    +         (rgs args (cdr rgs)))
    +        ((null? rgs) res))))
    +
    +;@
    +(define logand
    +  (letrec
    +      ((lgand
    +        (lambda (n2 n1 scl acc)
    +          (cond ((= n1 n2) (+ acc (* scl n1)))
    +                ((zero? n2) acc)
    +                ((zero? n1) acc)
    +                (else (lgand (logical:ash-4 n2)
    +                             (logical:ash-4 n1)
    +                             (* 16 scl)
    +                             (+ (* (vector-ref (vector-ref logical:boole-and
    +                                                           (modulo n1 16))
    +                                               (modulo n2 16))
    +                                   scl)
    +                                acc)))))))
    +    (logical:reduce lgand -1)))
    +;@
    +(define logior
    +  (letrec
    +      ((lgior
    +        (lambda (n2 n1 scl acc)
    +          (cond ((= n1 n2) (+ acc (* scl n1)))
    +                ((zero? n2) (+ acc (* scl n1)))
    +                ((zero? n1) (+ acc (* scl n2)))
    +                (else (lgior (logical:ash-4 n2)
    +                             (logical:ash-4 n1)
    +                             (* 16 scl)
    +                             (+ (* (- 15 (vector-ref
    +                                          (vector-ref logical:boole-and
    +                                                      (- 15 (modulo n1 16)))
    +                                          (- 15 (modulo n2 16))))
    +                                   scl)
    +                                acc)))))))
    +    (logical:reduce lgior 0)))
    +;@
    +(define logxor
    +  (letrec
    +      ((lgxor
    +        (lambda (n2 n1 scl acc)
    +          (cond ((= n1 n2) acc)
    +                ((zero? n2) (+ acc (* scl n1)))
    +                ((zero? n1) (+ acc (* scl n2)))
    +                (else (lgxor (logical:ash-4 n2)
    +                             (logical:ash-4 n1)
    +                             (* 16 scl)
    +                             (+ (* (vector-ref (vector-ref logical:boole-xor
    +                                                           (modulo n1 16))
    +                                               (modulo n2 16))
    +                                   scl)
    +                                acc)))))))
    +    (logical:reduce lgxor 0)))
    +;@
    +(define (lognot n) (- -1 n))
    +;@
    +(define (logtest n1 n2)
    +  (not (zero? (logand n1 n2))))
    +;@
    +(define (logbit? index n)
    +  (logtest (expt 2 index) n))
    +;@
    +(define (copy-bit index to bool)
    +  (if bool
    +      (logior to (arithmetic-shift 1 index))
    +      (logand to (lognot (arithmetic-shift 1 index)))))
    +;@
    +(define (bitwise-if mask n0 n1)
    +  (logior (logand mask n0)
    +          (logand (lognot mask) n1)))
    +;@
    +(define (bit-field n start end)
    +  (logand (lognot (ash -1 (- end start)))
    +          (arithmetic-shift n (- start))))
    +;@
    +(define (copy-bit-field to from start end)
    +  (bitwise-if (arithmetic-shift (lognot (ash -1 (- end start))) start)
    +              (arithmetic-shift from start)
    +              to))
    +;@
    +(define (rotate-bit-field n count start end)
    +  (define width (- end start))
    +  (set! count (modulo count width))
    +  (let ((mask (lognot (ash -1 width))))
    +    (define zn (logand mask (arithmetic-shift n (- start))))
    +    (logior (arithmetic-shift
    +             (logior (logand mask (arithmetic-shift zn count))
    +                     (arithmetic-shift zn (- count width)))
    +             start)
    +            (logand (lognot (ash mask start)) n))))
    +;@
    +(define (arithmetic-shift n count)
    +  (if (negative? count)
    +      (let ((k (expt 2 (- count))))
    +        (if (negative? n)
    +            (+ -1 (quotient (+ 1 n) k))
    +            (quotient n k)))
    +      (* (expt 2 count) n)))
    +;@
    +(define integer-length
    +  (letrec ((intlen (lambda (n tot)
    +                     (case n
    +                       ((0 -1) (+ 0 tot))
    +                       ((1 -2) (+ 1 tot))
    +                       ((2 3 -3 -4) (+ 2 tot))
    +                       ((4 5 6 7 -5 -6 -7 -8) (+ 3 tot))
    +                       (else (intlen (logical:ash-4 n) (+ 4 tot)))))))
    +    (lambda (n) (intlen n 0))))
    +;@
    +(define logcount
    +  (letrec ((logcnt (lambda (n tot)
    +                     (if (zero? n)
    +                         tot
    +                         (logcnt (quotient n 16)
    +                                 (+ (vector-ref
    +                                     '#(0 1 1 2 1 2 2 3 1 2 2 3 2 3 3 4)
    +                                     (modulo n 16))
    +                                    tot))))))
    +    (lambda (n)
    +      (cond ((negative? n) (logcnt (lognot n) 0))
    +            ((positive? n) (logcnt n 0))
    +            (else 0)))))
    +;@
    +(define (log2-binary-factors n)
    +  (+ -1 (integer-length (logand n (- n)))))
    +
    +(define (bit-reverse k n)
    +  (do ((m (if (negative? n) (lognot n) n) (arithmetic-shift m -1))
    +       (k (+ -1 k) (+ -1 k))
    +       (rvs 0 (logior (arithmetic-shift rvs 1) (logand 1 m))))
    +      ((negative? k) (if (negative? n) (lognot rvs) rvs))))
    +;@
    +(define (reverse-bit-field n start end)
    +  (define width (- end start))
    +  (let ((mask (lognot (ash -1 width))))
    +    (define zn (logand mask (arithmetic-shift n (- start))))
    +    (logior (arithmetic-shift (bit-reverse width zn) start)
    +            (logand (lognot (ash mask start)) n))))
    +;@
    +(define (integer->list k . len)
    +  (if (null? len)
    +      (do ((k k (arithmetic-shift k -1))
    +           (lst '() (cons (odd? k) lst)))
    +          ((<= k 0) lst))
    +      (do ((idx (+ -1 (car len)) (+ -1 idx))
    +           (k k (arithmetic-shift k -1))
    +           (lst '() (cons (odd? k) lst)))
    +          ((negative? idx) lst))))
    +;@
    +(define (list->integer bools)
    +  (do ((bs bools (cdr bs))
    +       (acc 0 (+ acc acc (if (car bs) 1 0))))
    +      ((null? bs) acc)))
    +(define (booleans->integer . bools)
    +  (list->integer bools))
    +
    +;;;;@ SRFI-60 aliases
    +(define ash arithmetic-shift)
    +(define bitwise-ior logior)
    +(define bitwise-xor logxor)
    +(define bitwise-and logand)
    +(define bitwise-not lognot)
    +(define bit-count logcount)
    +(define bit-set?   logbit?)
    +(define any-bits-set? logtest)
    +(define first-set-bit log2-binary-factors)
    +(define bitwise-merge bitwise-if)
    +
    +;;; Legacy
    +;;(define (logical:rotate k count len) (rotate-bit-field k count 0 len))
    +;;(define (logical:ones deg) (lognot (ash -1 deg)))
    +;;(define integer-expt expt)            ; legacy name
    +
    + + +

    Copyright

    +

    Copyright (C) Aubrey Jaffer (2004, 2005). All Rights Reserved.

    + +

    +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: +

    +

    +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. +

    +

    +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +

    + + +
    +
    Editor: David Van Horn
    + + +Last modified: Sat Jan 29 13:16:05 EST 2005 + + + diff --git a/doc/srfi-std/srfi-61.html b/doc/srfi-std/srfi-61.html new file mode 100644 index 0000000000..db64d63488 --- /dev/null +++ b/doc/srfi-std/srfi-61.html @@ -0,0 +1,190 @@ + + + + SRFI 61: A more general COND clause + + + + + +

    Title

    + +

    A more general cond clause

    + + +

    Author

    + +

    Taylor Campbell

    + + +

    Status

    +

    This SRFI is currently in ``final'' status. To see an explanation of +each status that a SRFI can hold, see here. +You can access previous messages via +the archive of the mailing list.

    +
    • Received: 2005/01/04
    • +
    • Draft: 2005/01/03-2005/03/04
    • +
    • Final: 2005/07/21
    • +
    + + +

    Abstract

    + +

    This SRFI proposes an extension to the cond syntax to allow a more +general clause, one that allows binding the results of tests as in the +=> clauses and user-defined meaning of the success & failure of tests.

    + + +

    Rationale

    + +

    The present set of cond clauses is based on simple boolean testing. It +is prohibitively inexpressive in that the condition part of a cond +clause that uses => may pass only a single value to the receiver, and +it enforces a semantics whereby #f implies failure of the condition. +Programmers frequently use different tokens to imply failure, such as +in R5RS's I/O readers which return a distinguished 'EOF object' to +denote failure, and a successful condition may produce more than one +useful value. This simple extension allows any meaning of 'failure' to +be assigned on a per-clause basis, and it also allows the condition to +return multiple values to be passed to the receiver.

    + + +

    Specification

    + +

    The <cond clause> production in the formal syntax of Scheme as written +by R5RS in section 7.1.3 is extended with a new option:

    + +
    +
      <cond clause> --->
    +      ...
    +    | (<generator> <guard> => <receiver>)
    +
    + +

    where <generator>, <guard>, & <receiver> are all <expression>s.

    + +

    Clauses of this form have the following semantics: <generator> is +evaluated. It may return arbitrarily many values. <Guard> is applied +to an argument list containing the values in order that <generator> +returned. If <guard> returns a true value for that argument list, +<receiver> is applied with an equivalent argument list. If <guard> +returns a false value, however, the clause is abandoned and the next +one is tried.

    + + +

    Examples

    + +

    This port->char-list procedure accepts an input port and returns a list +of all the characters it produces until the end.

    + +
    +
      (define (port->char-list port)
    +    (cond ((read-char port) char?
    +           => (lambda (c) (cons c (port->char-list port))))
    +          (else '())))
    +
    + +

    Consider now a hypothetical table-entry procedure that accepts two +arguments, a table (perhaps a hash table) and a key to an entry that +may be in the table; it returns two values: a boolean that denotes +whether or not an entry with the given key was in the table and, if it +was, the value associated with the key. Also, a hypothetical proj0 +combinator (projection of argument 0) returns its 0th argument and +ignores all others. One might conditionally branch to a certain body +of code if the table contains the desired entry like so with the new +type of cond clause:

    + +
    +
      (cond ...
    +        ((table-entry <table> <key>) proj0
    +         => (lambda (present? value)
    +              ...[VALUE is bound to the value of the entry]...))
    +        ...)
    +
    + + +

    Implementation

    + +

    The entirety of a syntax transformer for the new cond syntax is given +here. It uses an auxiliary macro, cond/maybe-more, to simplify the +construction of if expressions with or without more cond clauses. The +code is in the public domain.

    + +
    +
    (define-syntax cond
    +  (syntax-rules (=> ELSE)
    +
    +    ((COND (ELSE else1 else2 ...))
    +     ;; The (IF #T (BEGIN ...)) wrapper ensures that there may be no
    +     ;; internal definitions in the body of the clause.  R5RS mandates
    +     ;; this in text (by referring to each subform of the clauses as
    +     ;; <expression>) but not in its reference implementation of COND,
    +     ;; which just expands to (BEGIN ...) with no (IF #T ...) wrapper.
    +     (IF #T (BEGIN else1 else2 ...)))
    +
    +    ((COND (test => receiver) more-clause ...)
    +     (LET ((T test))
    +       (COND/MAYBE-MORE T
    +                        (receiver T)
    +                        more-clause ...)))
    +
    +    ((COND (generator guard => receiver) more-clause ...)
    +     (CALL-WITH-VALUES (LAMBDA () generator)
    +       (LAMBDA T
    +         (COND/MAYBE-MORE (APPLY guard    T)
    +                          (APPLY receiver T)
    +                          more-clause ...))))
    +
    +    ((COND (test) more-clause ...)
    +     (LET ((T test))
    +       (COND/MAYBE-MORE T T more-clause ...)))
    +
    +    ((COND (test body1 body2 ...) more-clause ...)
    +     (COND/MAYBE-MORE test
    +                      (BEGIN body1 body2 ...)
    +                      more-clause ...))))
    +
    +(define-syntax cond/maybe-more
    +  (syntax-rules ()
    +    ((COND/MAYBE-MORE test consequent)
    +     (IF test
    +         consequent))
    +    ((COND/MAYBE-MORE test consequent clause ...)
    +     (IF test
    +         consequent
    +         (COND clause ...)))))
    +
    + + +

    Copyright

    + +

    Copyright (C) 2004 Taylor Campbell. All rights reserved.

    + +

    +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: +

    +

    +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. +

    +

    +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +

    + + +
    +
    Editor: Mike Sperber
    + + + diff --git a/doc/srfi-std/srfi-62.html b/doc/srfi-std/srfi-62.html new file mode 100644 index 0000000000..304071e909 --- /dev/null +++ b/doc/srfi-std/srfi-62.html @@ -0,0 +1,211 @@ + +SRFI 62: S-expression comments + +

    Title

    + +S-expression comments + +

    Author

    + +Taylor Campbell + +

    Status

    +This SRFI is currently in ``final'' status. To see an explanation of each status that a SRFI can hold, see here. +You can access previous messages via the archive of the mailing list. +

    + +

    Abstract

    + +

    +This SRFI proposes a simple extension to Scheme's lexical syntax that +allows individual S-expressions to be made into comments, ignored by +the reader. This contrasts with the standard Lisp semicolon commnets, +which make the reader ignore the remainder of the line, and the +slightly less common block comments, as SRFI 30 +defines: both of these mechanisms comment out slices of text, not +S-expressions. +

    + +

    Rationale

    + +

    +Line and block comments are useful for embedding textual commentary in +programs, but they are not conducive to commenting out code easily in +an absence of extensive editor support for removing selected text that +composes S-expressions while retaining them in the text itself, or +subsequently removing the comments and re-introducing the S-expressions +themselves. +

    + +

    Informal specification

    + +

    +A new octothorpe reader syntax character is defined, #\;, such +that the reader ignores the S-expression following the #; and +proceeds on to the S-expression after that. For example, +
    +

    + + + + + + + + + + + + + + + + + + + + + +
    (+ 1 #;(* 2 3) 4)-reads-> (+ 1 4)-evals-> 5
    (list 'x #;'y 'z)-reads-> (list (quote x) (quote z))-evals-> (x z)
    (* 3 4 #;(+ 1 2))-reads-> (* 3 4)-evals-> 12
    (#;sqrt abs -16)-reads-> (abs -16)-evals-> 16
    + +

    +Some examples of nested S-expression comments may appear confusing at +first, but they are straightforwardly explained. For instance, +consider the text (list 'a #; #;'b 'c 'd). This reads as the +list represented by (list (quote a) (quote d)). Note that +both 'b and 'c seemed to 'disappear.' The reason is +simply that when the first #; causes the reader to read ahead +in the input stream for the next S-expression, the reader encounters +another #;, which causes the 'b to be consumed, and +which then moves the reader on to 'c to return as the first +S-expression following the first #;. Since it is the first +S-expression following a #;, 'b is ignored as well, +leaving only 'd. +

    + +

    +That is a fairly special case of nested S-expression comments. Others +are somewhat simpler for intuition to grasp immediately, such as: +
    +
    +(list 'a #;(list 'b #;c 'd) 'e) -reads-> (list (quote a) (quote e)) -evals-> (a e) +
    +
    +There are also some other somewhat peculiar examples, such as in dotted +lists and at the end of lists, which are still simple to grasp: +
    +
    + + + + + + + + + +
    '(a . #;b c)-reads-> (quote (a . c))
    '(a . b #;c)-reads-> (quote (a . b))
    +

    + +

    +Note, however, that any text that is invalid without S-expression +comments will be invalid with them as well, and an S-expression comment +prefix, #;, must be followed by a complete S-expression (and +after that either a complete S-expression or a special token such as a +closing parenthesis, a dot in dotted lists, or the end of file); for +instance, the following are all errors: +
    +
    +

      +
    • (#;a . b)
    • +
    • (a . #;b)
    • +
    • (a #;. b)
    • +
    • (#;x #;y . z)
    • +
    • (#; #;x #;y . z)
    • +
    • (#; #;x . z)
    • +
    +

    + +

    Formal specification

    + +

    +R5RS's formal syntax is modified as follows: +

      +
    • In section 7.1.1, a #; option is added to the + <token> non-terminal.
    • +
    • In section 7.1.2, a non-terminal <commented datum> + is defined: +
        <commented datum> ---> "#;" <datum> <datum>
      +
    • +
    • Also in section 7.1.2, the <datum> non-terminal is + modified to have a <commented datum> option.
    • +
    • Finally in section 7.1.2, the <list> and + <vector> non-terminals are replaced with the + following rules, along with two auxiliary ones: +
        <list> ---> "(" <datum>* <optional dot> <delimiter prefix> ")"
      +  <vector> ---> "#(" <datum>* <delimiter prefix> ")"
      +  <optional dot> ---> <empty> | <datum> <delimiter prefix> "." <datum>
      +  <delimiter prefix> ---> <empty> | "#;" <datum> <delimiter prefix>
      +
    • +
    +The first datum in a <commented datum> is ignored +semantically, as is any datum immediately following a #; token +in a delimiter prefix. +

    +All of the new or modified rules are presented here: +
      7.1.1:
    +
    +    <token> ---> <identifier> | <boolean> | <number>
    +        | <character> | <string>
    +        | "(" | ")" | "#(" | "'" | "`" | "," | ",@" | "." | "#;"
    +
    +  7.1.2:
    +
    +    <datum> ---> <simple datum> | <compound datum>
    +        | <commented datum>
    +    <commented datum> ---> "#;" <datum> <datum>
    +    <list> ---> "(" <datum>* <optional dot> <delimiter prefix> ")"
    +    <vector> ---> "#(" <datum>* <delimiter prefix> ")"
    +    <optional dot> ---> <empty> | <datum> <delimiter prefix> "." <datum>
    +    <delimiter prefix> ---> <empty> | "#;" <datum> <delimiter prefix>
    +
    +

    + +

    Copyright

    + +

    Copyright (C) 2004 Taylor Campbell. All rights reserved.

    + +

    +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: +

    +

    +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. +

    +

    +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +

    + + +
    +
    Editor: Mike Sperber
    + + diff --git a/doc/srfi-std/srfi-63.html b/doc/srfi-std/srfi-63.html new file mode 100644 index 0000000000..d846e0f71b --- /dev/null +++ b/doc/srfi-std/srfi-63.html @@ -0,0 +1,1516 @@ + + + + SRFI 63: Homogeneous and Heterogeneous Arrays + + + + +

    Title

    + +Homogeneous and Heterogeneous Arrays + +

    Author

    + +Aubrey Jaffer + +

    Status

    + +This SRFI is currently in ``final'' status. To see an explanation of each +status that a SRFI can hold, see here. You can +access previous messages via the +archive of the mailing list. +

    +

    + +

    Abstract

    + +The SRFI, which is to supersede +SRFI-47, +"Array", + +
      + +
    • synthesizes array concepts from Common-Lisp and Alan Bawden's + "array.scm"; + +
    • incorporates all the uniform vector types from + SFRI-4 + "Homogeneous numeric vector datatypes"; + +
    • adds a boolean uniform array type; + +
    • adds 16.bit and 128.bit floating-point uniform-array types; + +
    • adds decimal floating-point uniform-array types; and + +
    • adds array types of (dual) floating-point complex numbers. + +
    + +Multi-dimensional arrays subsume homogeneous vectors as the +one-dimensional case, obviating the need for SRFI-4. +

    +SRFI-58 gives a read/write invariant syntax for the homogeneous and +heterogeneous arrays described here. +

    + +

    Issues

    + +
      + + + + +
    • +The conversion rules for exact decimal +flonums have yet to be determined. Wisdom in this area would come +from experience. Lacking that, it is better to underspecify the +behavior of decimal flonums than to make it wrong. +

      + + + + +

    + +

    Rationale

    + +

    Arrays

    + +Computations have been organized into multidimensional arrays for over +200 years. Applications for multi-dimensional arrays continue to +arise. Computer graphics and imaging, whether vector or raster based, +use arrays. A general-purpose computer language without +multidimensional arrays is an oxymoron. + +

    Precision

    + +R5RS provides an input syntax for inexact numbers which is capable of +distinguishing between short, single, +double, and long precisions. But R5RS provides +no method for limiting the precision of calculations: + +
    +In particular, implementations that use flonum representations must +follow these rules: A flonum result must be represented with at least +as much precision as is used to express any of the inexact arguments +to that operation. + +
    + +And calculation with any exact number inputs blows the precision out +to "the most precise flonum format available": + +
    +If, however, an exact number is operated upon so as to produce an +inexact result (as by `sqrt'), and if the result is +represented as a flonum, then the most precise flonum format available +must be used; but if the result is represented in some other way then +the representation must have at least as much precision as the most +precise flonum format available. + +
    + +Scheme is not much hampered by lack of low-precision inexact numbers +for scalar calculations. The extra computation incurred by gratuitous +precision is usually small compared with the overhead of type-dispatch +and boxed data manipulation. +

    +

    Homogeneous Arrays

    + +But if calculations are vectorized, that overhead can become +significant. Sophisticated program analysis may be able to deduce +that aggregated number storage can be made uniformly of the most +precise flonum format available. But even the most aggressive +analysis of uncontrived programs will not be able to reduce the +precision while yielding results equivalent to the most precise +calculation, as R5RS requires. +

    + + + + +Also significant is that the numerical data in most Scheme +implementations has manifest type information encoded with it. +Varying sizes of number objects means that the vectors hold pointers +to some numbers, requiring data fetches from memory locations unlikely +to be in the same CPU cache-line. +

    +Arrays composed of elements all having the same size representations +can eliminate these indirect accesses and the storage allocation +associated with them. Homogeneous arrays of lower precision flonums +can reduce by factors of 2 or 4 the storage they occupy; which can +also speed execution because of the lower bandwidth to the memory +necessary to supply the CPU data cache. +

    +

    Common Lisp

    + +Common-Lisp arrays are serviceable, and are the basis for arrays here. +Common-Lisp's make-array does not translate well to +Scheme because the array element type and the initial contents are +passed using named arguments. +

    +Prototype arrays specify both the homogeneous array type (or lack of) +and the initial value or lack of it; allowing these purposes to be +satisfied by one argument to make-array or other +procedures which create arrays. +

    +Some have objected that restricting type specification to arrays is a +half-measure. In vectorized programs, specifying the precision of +scalar calculations will produce negligible performance improvements. +But the performance improvements of homogeneous arrays can accrue to +both interpreted and compiled Scheme implementations. By avoiding the +morass of general type specification, SRFI-63 can be more easily +accommodated by more Scheme implementations. +

    +

    Argument Order

    + +
      +
    • +Most of the procedures originate from Alan Bawden's "array.scm". +SRFI-47's array-set! argument order is that of Bawden's +package. SLIB +adopted "array.scm" in 1993. This form of array-set! has +also been part of the +SCM Scheme +implementation since 1993.

      + +

    • +The array-set! argument order is different from the +same-named procedure in +SRFI-25. +Type dispatch on the first argument to array-set! could +support both SRFIs simultaneously.

      + +

    • +The make-array arguments are different from the +same-named procedure in +SRFI-25. +Type dispatch on the first argument to make-array could +support both SRFIs simultaneously.

      + +

    • +The SRFI-47 argument orders are motivated to make easy dealing with +the variable arity resulting from variable rank. + +
             (vector->array  vect  proto  bound1 ...)
      +          (make-array        proto  bound1 ...)
      +   (make-shared-array  array mapper bound1 ...)
      +          (array-set!  array obj    index1 ...)
      +    (array-in-bounds?  array        index1 ...)
      +           (array-ref  array        index1 ...)
      +
      +

      +The list->array is somewhat dissonant: +

               (list->array  rank  proto  list)
      +
      +

      +

    +

    + +

    Homogeneous Array Types

    + +All implementations must support Scheme strings as rank 1 character +arrays. This requirement mandates that Scheme strings be valid +arguments to array procedures; their stored representations may be +different from other character arrays. +

    + +Although an implementation is required to define all the prototype +functions, it is not required to support all or even any of the +homogeneous numeric arrays. It is assumed that no uniform numeric +types have larger precision than the Scheme implementation supports as +numbers. +

    + + +
    prototype
    procedure +
    exactness + element type +
    vector any +
    A:floC128binexact128.bit binary flonum complex +
    A:floC64b inexact64.bit binary flonum complex +
    A:floC32b inexact32.bit binary flonum complex +
    A:floC16b inexact16.bit binary flonum complex +
    A:floR128binexact128.bit binary flonum real +
    A:floR64b inexact64.bit binary flonum real +
    A:floR32b inexact32.bit binary flonum real +
    A:floR16b inexact16.bit binary flonum real +
    +
    A:floQ128dexact128.bit decimal flonum rational +
    A:floQ64d exact64.bit decimal flonum rational +
    A:floQ32d exact32.bit decimal flonum rational +
    +
    A:fixZ64b exact64.bit binary fixnum +
    A:fixZ32b exact32.bit binary fixnum +
    A:fixZ16b exact16.bit binary fixnum +
    A:fixZ8b exact8.bit binary fixnum +
    A:fixN64b exact64.bit nonnegative binary fixnum +
    A:fixN32b exact32.bit nonnegative binary fixnum +
    A:fixN16b exact16.bit nonnegative binary fixnum +
    A:fixN8b exact8.bit nonnegative binary fixnum +
    A:bool boolean +
    string char +
    +

    +Decimal flonums are used for financial calculations so that fractional +errors do not accumulate. They should be exact numbers. +

    + +

    Conversions

    + +
      +
    • All the elements of arrays of type A:fixN8b, A:fixN16b, + A:fixN32b, A:fixN64b, A:fixZ8b, A:fixZ16b, A:fixZ32b, or + A:fixZ64b are exact.

      + +

    • All the elements of arrays of type A:floR16b, A:floR32b, + A:floR64b, A:floR128b, A:floC16b, A:floC32b, A:floC64b, and + A:floC128b are inexact.

      + +

    • The value retrieved from an exact array element will equal (=) + the value stored in that element.

      + +

    • Assigning a non-integer to array-type A:fixN8b, A:fixN16b, + A:fixN32b, A:fixN64b, A:fixZ8b, A:fixZ16b, A:fixZ32b, or + A:fixZ64b is an error.

      + +

    • Assigning a number larger than can be represented in array-type + A:fixN8b, A:fixN16b, A:fixN32b, A:fixN64b, A:fixZ8b, A:fixZ16b, + A:fixZ32b, or A:fixZ64b is an error.

      + +

    • Assigning a negative number to array-type A:fixN8b, A:fixN16b, + A:fixN32b, or A:fixN64b is an error.

      + +

    • Assigning an inexact number to array-type A:fixN8b, A:fixN16b, + A:fixN32b, A:fixN64b, A:fixZ8b, A:fixZ16b, A:fixZ32b, or + A:fixZ64b is an error.

      + +

    • When assigning an exact number to an inexact array-type, the + procedure may report a violation of an implementation + restriction.

      + +

    • Assigning a non-real number (eg. real? returns + #f) to an A:floR128b, A:floR64b, A:floR32b, or + A:floR16b array is an error.

      + +

    • When an inexact number is assigned to an array whose type is + lower precision, the number will be rounded to that lower + precision if possible; otherwise it is an error.

      + +

    + + +

    Prototype Procedures

    + +Implementations are required to define all of the prototype +procedures. Uniform types of matching format and sizes which the +platform supports will be used; the others will be represented as +follows: +

    +For inexact flonum complex arrays: +

      +
    • the next larger complex format is used; +
    • if there is no larger format, +
        +
      • then if the implementation supports complex floating-point numbers of + unbounded precision, +
          +
        • then a heterogeneous array; +
        • else the largest inexact flonum complex array. +
        +
      +
    + +For inexact flonum real arrays: +
      +
    • the next larger real format is used; +
    • if there is no larger real format, then the next larger complex format + is used. +
    • If there is no larger complex format, +
        +
      • then if the implementation supports floating-point real numbers of + unbounded precision, +
          +
        • then a heterogeneous array; +
        • else the largest inexact flonum real or complex array. +
        +
      +
    + +For exact decimal flonum arrays: +
      +
    • the next larger decimal flonum format array is used; +
    • If there is no larger decimal flonum format, then a + heterogeneous array is used. +
    + +For exact bipolar fixnum arrays: +
      +
    • the next larger bipolar fixnum format array is used; +
    • If there is no larger bipolar fixnum format, +
        +
      • then if the implementation supports exact integers of unbounded + precision, +
          +
        • then a heterogeneous array; +
        • else the largest bipolar fixnum array. +
        +
      +
    + +For exact nonnegative fixnum arrays: +
      +
    • the next larger nonnegative fixnum format array is used; +
    • If there is no larger nonnegative fixnum format, +
        +
      • then the next larger bipolar fixnum format is used. +
      • If there is no larger bipolar fixnum format, +
          +
        • then if the implementation supports exact integers of + unbounded precision, +
            +
          • then a heterogeneous array; +
          • else the largest nonnegative or bipolar fixnum array. +
          +
        +
      +
    + +

    +Note that these rules are used to configure an implementation's +definitions of the prototype procedures, which should not themselves +be type-dispatching. +

    +This arrangement has platforms which support uniform array types +employing them, with less capable platforms using vectors; but all +working compatibly from the same source code. +

    + +

    Shared Arrays

    + +To my knowledge, the specification of shared array index mapping by +means of a procedure is original to Alan Bawden in his "array.scm". +Make-shared-array creates any view into an array whose +coordinates can be mapped by exact integer affine functions. The rank +of the arrays need not match. Shared arrays are quite useful. They +can reverse indexes, make subarrays, and facilitate straightforward +implementations of divide-and-conquer algorithms. +

    +In Common-Lisp a displaced array can be created by calls to +adjust-array. + +But displaced arrays are far less flexible than shared +arrays, constrained to have the same rank as the original and +allowing only index displacements (not reversals, skips, or +shuffling). +

    + +

    Limit Cases

    + +The bounds for each index in both Alan Bawden's "array.scm" and +SRFI-25 + +can be any consecutive run of integers. All indexes in SRFI-63 are +zero-based for compatibility with R5RS. +

    +Empty arrays having no elements can be of any positive rank. Empty +arrays can be returned from make-shared-array. +

    +Following Common-Lisp's +lead, zero-rank arrays have a single element. +

    +Except for character arrays, array access time is +O(R)+V, where R is the rank of the array and +V is the vector access time. +

    +Character array access time is +O(R)+S, where R is the rank of the array and +S is the string access time. +

    + +

    Specification

    + + +

    +

    +
    Function: array? obj +
    + + +

    +Returns #t if the obj is an array, and #f if not. +

    + + +

    +Note: Arrays are not disjoint from other Scheme types. +Vectors and possibly strings also satisfy array?. +A disjoint array predicate can be written: + + + +

    (define (strict-array? obj)
    +  (and (array? obj) (not (string? obj)) (not (vector? obj))))
    +
    + +

    +

    +
    Function: equal? obj1 obj2 +
    + + +

    +Returns #t if obj1 and obj2 have the same rank and dimensions and the +corresponding elements of obj1 and obj2 are equal?. + + +

    +equal? recursively compares the contents of pairs, vectors, strings, and +arrays, applying eqv? on other objects such as numbers +and symbols. A rule of thumb is that objects are generally equal? if +they print the same. equal? may fail to terminate if its arguments are +circular data structures. + + + +

    (equal? 'a 'a)                             =>  #t
    +(equal? '(a) '(a))                         =>  #t
    +(equal? '(a (b) c)
    +        '(a (b) c))                        =>  #t
    +(equal? "abc" "abc")                       =>  #t
    +(equal? 2 2)                               =>  #t
    +(equal? (make-vector 5 'a)
    +        (make-vector 5 'a))                =>  #t
    +(equal? (make-array (A:fixN32b 4) 5 3)
    +        (make-array (A:fixN32b 4) 5 3))    =>  #t
    +(equal? (make-array '#(foo) 3 3)
    +        (make-array '#(foo) 3 3))          =>  #t
    +(equal? (lambda (x) x)
    +        (lambda (y) y))                    =>  unspecified
    +
    + +
    + +

    +

    +
    Function: array-rank obj +
    + + +

    +Returns the number of dimensions of obj. If obj is not an array, 0 is +returned. +

    + + +

    +

    +
    Function: array-dimensions array +
    + + +

    +Returns a list of dimensions. + + + +

    (array-dimensions (make-array '#() 3 5))
    +   => (3 5)
    +
    + +
    + +

    +

    +
    Function: make-array prototype k1 ... +
    + + +

    +Creates and returns an array of type prototype with dimensions k1, ... +and filled with elements from prototype. prototype must be an array, vector, or +string. The implementation-dependent type of the returned array +will be the same as the type of prototype; except if that would be a vector +or string with rank not equal to one, in which case some variety of +array will be returned. + + +

    +If the prototype has no elements, then the initial contents of the returned +array are unspecified. Otherwise, the returned array will be filled +with the element at the origin of prototype. +

    + + +

    +

    +
    Function: make-shared-array array mapper k1 ... +
    + + +

    +make-shared-array can be used to create shared subarrays of other +arrays. The mapper is a function that translates coordinates in +the new array into coordinates in the old array. A mapper must be +linear, and its range must stay within the bounds of the old array, but +it can be otherwise arbitrary. A simple example: + + + +

    (define fred (make-array '#(#f) 8 8))
    +(define freds-diagonal
    +  (make-shared-array fred (lambda (i) (list i i)) 8))
    +(array-set! freds-diagonal 'foo 3)
    +(array-ref fred 3 3)
    +   => FOO
    +(define freds-center
    +  (make-shared-array fred (lambda (i j) (list (+ 3 i) (+ 3 j)))
    +                     2 2))
    +(array-ref freds-center 0 0)
    +   => FOO
    +
    + +
    + +

    +

    +
    Function: list->array rank proto list +
    + + +

    +list must be a rank-nested list consisting of all the elements, in +row-major order, of the array to be created. + + +

    +list->array returns an array of rank rank and type proto consisting of all the +elements, in row-major order, of list. When rank is 0, list is the lone +array element; not necessarily a list. + + + +

    (list->array 2 '#() '((1 2) (3 4)))
    +                => #2A((1 2) (3 4))
    +(list->array 0 '#() 3)
    +                => #0A 3
    +
    + +
    + +

    +

    +
    Function: array->list array +
    + + +

    +Returns a rank-nested list consisting of all the elements, in +row-major order, of array. In the case of a rank-0 array, array->list returns +the single element. + + + +

    (array->list #2A((ho ho ho) (ho oh oh)))
    +                => ((ho ho ho) (ho oh oh))
    +(array->list #0A ho)
    +                => ho
    +
    + +
    + +

    +

    +
    Function: vector->array vect proto dim1 ... +
    + + +

    +vect must be a vector of length equal to the product of exact +nonnegative integers dim1, .... + + +

    +vector->array returns an array of type proto consisting of all the elements, in +row-major order, of vect. In the case of a rank-0 array, vect has a +single element. + + + +

    (vector->array #(1 2 3 4) #() 2 2)
    +                => #2A((1 2) (3 4))
    +(vector->array '#(3) '#())
    +                => #0A 3
    +
    + +
    + +

    +

    +
    Function: array->vector array +
    + + +

    +Returns a new vector consisting of all the elements of array in +row-major order. + + + +

    (array->vector #2A ((1 2)( 3 4)))
    +                => #(1 2 3 4)
    +(array->vector #0A ho)
    +                => #(ho)
    +
    + +
    + +

    +

    +
    Function: array-in-bounds? array index1 ... +
    + + +

    +Returns #t if its arguments would be acceptable to +array-ref. +

    + + +

    +

    +
    Function: array-ref array k1 ... +
    + + +

    +Returns the (k1, ...) element of array. +

    + + +

    +

    +
    Procedure: array-set! array obj k1 ... +
    + + +

    +Stores obj in the (k1, ...) element of array. The value returned +by array-set! is unspecified. +

    + + +

    +These functions return a prototypical uniform-array enclosing the +optional argument (which must be of the correct type). If the +uniform-array type is supported by the implementation, then it is +returned; defaulting to the next larger precision type; resorting +finally to vector. + + +

    +

    +
    Function: a:floc128b z +
    + + +

    +

    Function: a:floc128b +
    +Returns an inexact 128.bit flonum complex uniform-array prototype. +
    + + +

    +

    +
    Function: a:floc64b z +
    + + +

    +

    Function: a:floc64b +
    +Returns an inexact 64.bit flonum complex uniform-array prototype. +
    + + +

    +

    +
    Function: a:floc32b z +
    + + +

    +

    Function: a:floc32b +
    +Returns an inexact 32.bit flonum complex uniform-array prototype. +
    + + +

    +

    +
    Function: a:floc16b z +
    + + +

    +

    Function: a:floc16b +
    +Returns an inexact 16.bit flonum complex uniform-array prototype. +
    + + +

    +

    +
    Function: a:flor128b z +
    + + +

    +

    Function: a:flor128b +
    +Returns an inexact 128.bit flonum real uniform-array prototype. +
    + + +

    +

    +
    Function: a:flor64b z +
    + + +

    +

    Function: a:flor64b +
    +Returns an inexact 64.bit flonum real uniform-array prototype. +
    + + +

    +

    +
    Function: a:flor32b z +
    + + +

    +

    Function: a:flor32b +
    +Returns an inexact 32.bit flonum real uniform-array prototype. +
    + + +

    +

    +
    Function: a:flor16b z +
    + + +

    +

    Function: a:flor16b +
    +Returns an inexact 16.bit flonum real uniform-array prototype. +
    + + +

    +

    +
    Function: a:flor128b z +
    + + +

    +

    Function: a:flor128b +
    +Returns an exact 128.bit decimal flonum rational uniform-array prototype. +
    + + +

    +

    +
    Function: a:flor64b z +
    + + +

    +

    Function: a:flor64b +
    +Returns an exact 64.bit decimal flonum rational uniform-array prototype. +
    + + +

    +

    +
    Function: a:flor32b z +
    + + +

    +

    Function: a:flor32b +
    +Returns an exact 32.bit decimal flonum rational uniform-array prototype. +
    + + +

    +

    +
    Function: a:fixz64b n +
    + + +

    +

    Function: a:fixz64b +
    +Returns an exact binary fixnum uniform-array prototype with at least +64 bits of precision. +
    + + +

    +

    +
    Function: a:fixz32b n +
    + + +

    +

    Function: a:fixz32b +
    +Returns an exact binary fixnum uniform-array prototype with at least +32 bits of precision. +
    + + +

    +

    +
    Function: a:fixz16b n +
    + + +

    +

    Function: a:fixz16b +
    +Returns an exact binary fixnum uniform-array prototype with at least +16 bits of precision. +
    + + +

    +

    +
    Function: a:fixz8b n +
    + + +

    +

    Function: a:fixz8b +
    +Returns an exact binary fixnum uniform-array prototype with at least +8 bits of precision. +
    + + +

    +

    +
    Function: a:fixn64b k +
    + + +

    +

    Function: a:fixn64b +
    +Returns an exact non-negative binary fixnum uniform-array prototype with at +least 64 bits of precision. +
    + + +

    +

    +
    Function: a:fixn32b k +
    + + +

    +

    Function: a:fixn32b +
    +Returns an exact non-negative binary fixnum uniform-array prototype with at +least 32 bits of precision. +
    + + +

    +

    +
    Function: a:fixn16b k +
    + + +

    +

    Function: a:fixn16b +
    +Returns an exact non-negative binary fixnum uniform-array prototype with at +least 16 bits of precision. +
    + + +

    +

    +
    Function: a:fixn8b k +
    + + +

    +

    Function: a:fixn8b +
    +Returns an exact non-negative binary fixnum uniform-array prototype with at +least 8 bits of precision. +
    + + +

    +

    +
    Function: a:bool bool +
    + + +

    +

    Function: a:bool +
    +Returns a boolean uniform-array prototype. +
    + + + +

    Implementation

    + +slib/array.scm +implements array procedures for R4RS or R5RS compliant Scheme +implementations with records as implemented by +slib/record.scm +or SRFI-9. +"array.scm" redefines equal? to handle +arrays. +

    +

    ;;;;"array.scm" Arrays for Scheme
    +; Copyright (C) 2001, 2003 Aubrey Jaffer
    +;
    +;Permission to copy this software, to modify it, to redistribute it,
    +;to distribute modified versions, and to use it for any purpose is
    +;granted, subject to the following restrictions and understandings.
    +;
    +;1.  Any copy made of this software must include this copyright notice
    +;in full.
    +;
    +;2.  I have made no warranty or representation that the operation of
    +;this software will be error-free, and I am under no obligation to
    +;provide any services, by way of maintenance, update, or otherwise.
    +;
    +;3.  In conjunction with products arising from the use of this
    +;material, there shall be no use of my name in any advertising,
    +;promotional, or sales literature without prior written consent in
    +;each case.
    +
    +;;@code{(require 'array)} or @code{(require 'srfi-63)}
    +;;@ftindex array
    +
    +(require 'record)
    +
    +(define array:rtd
    +  (make-record-type "array"
    +                    '(dimensions
    +                      scales            ;list of dimension scales
    +                      offset            ;exact integer
    +                      store             ;data
    +                      )))
    +
    +(define array:dimensions
    +  (let ((dimensions (record-accessor array:rtd 'dimensions)))
    +    (lambda (array)
    +      (cond ((vector? array) (list (vector-length array)))
    +            ((string? array) (list (string-length array)))
    +            (else (dimensions array))))))
    +
    +(define array:scales
    +  (let ((scales (record-accessor array:rtd 'scales)))
    +    (lambda (obj)
    +      (cond ((string? obj) '(1))
    +            ((vector? obj) '(1))
    +            (else (scales obj))))))
    +
    +(define array:store
    +  (let ((store (record-accessor array:rtd 'store)))
    +    (lambda (obj)
    +      (cond ((string? obj) obj)
    +            ((vector? obj) obj)
    +            (else (store obj))))))
    +
    +(define array:offset
    +  (let ((offset (record-accessor array:rtd 'offset)))
    +    (lambda (obj)
    +      (cond ((string? obj) 0)
    +            ((vector? obj) 0)
    +            (else (offset obj))))))
    +
    +(define array:construct
    +  (record-constructor array:rtd '(dimensions scales offset store)))
    +
    +;;@args obj
    +;;Returns @code{#t} if the @1 is an array, and @code{#f} if not.
    +(define array?
    +  (let ((array:array? (record-predicate array:rtd)))
    +    (lambda (obj) (or (string? obj) (vector? obj) (array:array? obj)))))
    +
    +;;@noindent
    +;;@emph{Note:} Arrays are not disjoint from other Scheme types.
    +;;Vectors and possibly strings also satisfy @code{array?}.
    +;;A disjoint array predicate can be written:
    +;;
    +;;@example
    +;;(define (strict-array? obj)
    +;;  (and (array? obj) (not (string? obj)) (not (vector? obj))))
    +;;@end example
    +
    +;;@body
    +;;Returns @code{#t} if @1 and @2 have the same rank and dimensions and the
    +;;corresponding elements of @1 and @2 are @code{equal?}.
    +
    +;;@body
    +;;@0 recursively compares the contents of pairs, vectors, strings, and
    +;;@emph{arrays}, applying @code{eqv?} on other objects such as numbers
    +;;and symbols.  A rule of thumb is that objects are generally @0 if
    +;;they print the same.  @0 may fail to terminate if its arguments are
    +;;circular data structures.
    +;;
    +;;@example
    +;;(equal? 'a 'a)                             @result{}  #t
    +;;(equal? '(a) '(a))                         @result{}  #t
    +;;(equal? '(a (b) c)
    +;;        '(a (b) c))                        @result{}  #t
    +;;(equal? "abc" "abc")                       @result{}  #t
    +;;(equal? 2 2)                               @result{}  #t
    +;;(equal? (make-vector 5 'a)
    +;;        (make-vector 5 'a))                @result{}  #t
    +;;(equal? (make-array (A:fixN32b 4) 5 3)
    +;;        (make-array (A:fixN32b 4) 5 3))    @result{}  #t
    +;;(equal? (make-array '#(foo) 3 3)
    +;;        (make-array '#(foo) 3 3))          @result{}  #t
    +;;(equal? (lambda (x) x)
    +;;        (lambda (y) y))                    @result{}  @emph{unspecified}
    +;;@end example
    +(define (equal? obj1 obj2)
    +  (cond ((eqv? obj1 obj2) #t)
    +        ((or (pair? obj1) (pair? obj2))
    +         (and (pair? obj1) (pair? obj2)
    +              (equal? (car obj1) (car obj2))
    +              (equal? (cdr obj1) (cdr obj2))))
    +        ((or (string? obj1) (string? obj2))
    +         (and (string? obj1) (string? obj2)
    +              (string=? obj1 obj2)))
    +        ((or (vector? obj1) (vector? obj2))
    +         (and (vector? obj1) (vector? obj2)
    +              (equal? (vector-length obj1) (vector-length obj2))
    +              (do ((idx (+ -1 (vector-length obj1)) (+ -1 idx)))
    +                  ((or (negative? idx)
    +                       (not (equal? (vector-ref obj1 idx)
    +                                    (vector-ref obj2 idx))))
    +                   (negative? idx)))))
    +        ((or (array? obj1) (array? obj2))
    +         (and (array? obj1) (array? obj2)
    +              (equal? (array:dimensions obj1) (array:dimensions obj2))
    +              (equal? (array:store obj1) (array:store obj2))))
    +        (else #f)))
    +
    +;;@body
    +;;Returns the number of dimensions of @1.  If @1 is not an array, 0 is
    +;;returned.
    +(define (array-rank obj)
    +  (if (array? obj) (length (array:dimensions obj)) 0))
    +
    +;;@args array
    +;;Returns a list of dimensions.
    +;;
    +;;@example
    +;;(array-dimensions (make-array '#() 3 5))
    +;;   @result{} (3 5)
    +;;@end example
    +(define array-dimensions array:dimensions)
    +
    +;;@args prototype k1 @dots{}
    +;;
    +;;Creates and returns an array of type @1 with dimensions @2, @dots{}
    +;;and filled with elements from @1.  @1 must be an array, vector, or
    +;;string.  The implementation-dependent type of the returned array
    +;;will be the same as the type of @1; except if that would be a vector
    +;;or string with rank not equal to one, in which case some variety of
    +;;array will be returned.
    +;;
    +;;If the @1 has no elements, then the initial contents of the returned
    +;;array are unspecified.  Otherwise, the returned array will be filled
    +;;with the element at the origin of @1.
    +(define (make-array prototype . dimensions)
    +  (define tcnt (apply * dimensions))
    +  (let ((store
    +         (if (string? prototype)
    +             (case (string-length prototype)
    +               ((0) (make-string tcnt))
    +               (else (make-string tcnt
    +                                  (string-ref prototype 0))))
    +             (let ((pdims (array:dimensions prototype)))
    +               (case (apply * pdims)
    +                 ((0) (make-vector tcnt))
    +                 (else (make-vector tcnt
    +                                    (apply array-ref prototype
    +                                           (map (lambda (x) 0) pdims)))))))))
    +    (define (loop dims scales)
    +      (if (null? dims)
    +          (array:construct dimensions (cdr scales) 0 store)
    +          (loop (cdr dims) (cons (* (car dims) (car scales)) scales))))
    +    (loop (reverse dimensions) '(1))))
    +;;@args prototype k1 @dots{}
    +;;@0 is an alias for @code{make-array}.
    +(define create-array make-array)
    +
    +;;@args array mapper k1 @dots{}
    +;;@0 can be used to create shared subarrays of other
    +;;arrays.  The @var{mapper} is a function that translates coordinates in
    +;;the new array into coordinates in the old array.  A @var{mapper} must be
    +;;linear, and its range must stay within the bounds of the old array, but
    +;;it can be otherwise arbitrary.  A simple example:
    +;;
    +;;@example
    +;;(define fred (make-array '#(#f) 8 8))
    +;;(define freds-diagonal
    +;;  (make-shared-array fred (lambda (i) (list i i)) 8))
    +;;(array-set! freds-diagonal 'foo 3)
    +;;(array-ref fred 3 3)
    +;;   @result{} FOO
    +;;(define freds-center
    +;;  (make-shared-array fred (lambda (i j) (list (+ 3 i) (+ 3 j)))
    +;;                     2 2))
    +;;(array-ref freds-center 0 0)
    +;;   @result{} FOO
    +;;@end example
    +(define (make-shared-array array mapper . dimensions)
    +  (define odl (array:scales array))
    +  (define rank (length dimensions))
    +  (define shape
    +    (map (lambda (dim) (if (list? dim) dim (list 0 (+ -1 dim)))) dimensions))
    +  (do ((idx (+ -1 rank) (+ -1 idx))
    +       (uvt (append (cdr (vector->list (make-vector rank 0))) '(1))
    +            (append (cdr uvt) '(0)))
    +       (uvts '() (cons uvt uvts)))
    +      ((negative? idx)
    +       (let ((ker0 (apply + (map * odl (apply mapper uvt)))))
    +         (array:construct
    +          (map (lambda (dim) (+ 1 (- (cadr dim) (car dim)))) shape)
    +          (map (lambda (uvt) (- (apply + (map * odl (apply mapper uvt))) ker0))
    +               uvts)
    +          (apply +
    +                 (array:offset array)
    +                 (map * odl (apply mapper (map car shape))))
    +          (array:store array))))))
    +
    +;;@args rank proto list
    +;;@3 must be a rank-nested list consisting of all the elements, in
    +;;row-major order, of the array to be created.
    +;;
    +;;@0 returns an array of rank @1 and type @2 consisting of all the
    +;;elements, in row-major order, of @3.  When @1 is 0, @3 is the lone
    +;;array element; not necessarily a list.
    +;;
    +;;@example
    +;;(list->array 2 '#() '((1 2) (3 4)))
    +;;                @result{} #2A((1 2) (3 4))
    +;;(list->array 0 '#() 3)
    +;;                @result{} #0A 3
    +;;@end example
    +(define (list->array rank proto lst)
    +  (define dimensions
    +    (do ((shp '() (cons (length row) shp))
    +         (row lst (car lst))
    +         (rnk (+ -1 rank) (+ -1 rnk)))
    +        ((negative? rnk) (reverse shp))))
    +  (let ((nra (apply make-array proto dimensions)))
    +    (define (l2ra dims idxs row)
    +      (cond ((null? dims)
    +             (apply array-set! nra row (reverse idxs)))
    +            ((if (not (eqv? (car dims) (length row)))
    +                 (slib:error 'list->array
    +                             'non-rectangular 'array dims dimensions))
    +             (do ((idx 0 (+ 1 idx))
    +                  (row row (cdr row)))
    +                 ((>= idx (car dims)))
    +               (l2ra (cdr dims) (cons idx idxs) (car row))))))
    +    (l2ra dimensions '() lst)
    +    nra))
    +
    +;;@args array
    +;;Returns a rank-nested list consisting of all the elements, in
    +;;row-major order, of @1.  In the case of a rank-0 array, @0 returns
    +;;the single element.
    +;;
    +;;@example
    +;;(array->list #2A((ho ho ho) (ho oh oh)))
    +;;                @result{} ((ho ho ho) (ho oh oh))
    +;;(array->list #0A ho)
    +;;                @result{} ho
    +;;@end example
    +(define (array->list ra)
    +  (define (ra2l dims idxs)
    +    (if (null? dims)
    +        (apply array-ref ra (reverse idxs))
    +        (do ((lst '() (cons (ra2l (cdr dims) (cons idx idxs)) lst))
    +             (idx (+ -1 (car dims)) (+ -1 idx)))
    +            ((negative? idx) lst))))
    +  (ra2l (array-dimensions ra) '()))
    +
    +;;@args vect proto dim1 @dots{}
    +;;@1 must be a vector of length equal to the product of exact
    +;;nonnegative integers @3, @dots{}.
    +;;
    +;;@0 returns an array of type @2 consisting of all the elements, in
    +;;row-major order, of @1.  In the case of a rank-0 array, @1 has a
    +;;single element.
    +;;
    +;;@example
    +;;(vector->array #(1 2 3 4) #() 2 2)
    +;;                @result{} #2A((1 2) (3 4))
    +;;(vector->array '#(3) '#())
    +;;                @result{} #0A 3
    +;;@end example
    +(define (vector->array vect prototype . dimensions)
    +  (define vdx (vector-length vect))
    +  (if (not (eqv? vdx (apply * dimensions)))
    +      (slib:error 'vector->array vdx '<> (cons '* dimensions)))
    +  (let ((ra (apply make-array prototype dimensions)))
    +    (define (v2ra dims idxs)
    +      (cond ((null? dims)
    +             (set! vdx (+ -1 vdx))
    +             (apply array-set! ra (vector-ref vect vdx) (reverse idxs)))
    +            (else
    +             (do ((idx (+ -1 (car dims)) (+ -1 idx)))
    +                 ((negative? idx) vect)
    +               (v2ra (cdr dims) (cons idx idxs))))))
    +    (v2ra dimensions '())
    +    ra))
    +
    +;;@args array
    +;;Returns a new vector consisting of all the elements of @1 in
    +;;row-major order.
    +;;
    +;;@example
    +;;(array->vector #2A ((1 2)( 3 4)))
    +;;                @result{} #(1 2 3 4)
    +;;(array->vector #0A ho)
    +;;                @result{} #(ho)
    +;;@end example
    +(define (array->vector ra)
    +  (define dims (array-dimensions ra))
    +  (let* ((vdx (apply * dims))
    +         (vect (make-vector vdx)))
    +    (define (ra2v dims idxs)
    +      (if (null? dims)
    +          (let ((val (apply array-ref ra (reverse idxs))))
    +            (set! vdx (+ -1 vdx))
    +            (vector-set! vect vdx val)
    +            vect)
    +          (do ((idx (+ -1 (car dims)) (+ -1 idx)))
    +              ((negative? idx) vect)
    +            (ra2v (cdr dims) (cons idx idxs)))))
    +    (ra2v dims '())))
    +
    +(define (array:in-bounds? array indices)
    +  (do ((bnds (array:dimensions array) (cdr bnds))
    +       (idxs indices (cdr idxs)))
    +      ((or (null? bnds)
    +           (null? idxs)
    +           (not (integer? (car idxs)))
    +           (not (< -1 (car idxs) (car bnds))))
    +       (and (null? bnds) (null? idxs)))))
    +
    +;;@args array index1 @dots{}
    +;;Returns @code{#t} if its arguments would be acceptable to
    +;;@code{array-ref}.
    +(define (array-in-bounds? array . indices)
    +  (array:in-bounds? array indices))
    +
    +;;@args array k1 @dots{}
    +;;Returns the (@2, @dots{}) element of @1.
    +(define (array-ref array . indices)
    +  (define store (array:store array))
    +  (or (array:in-bounds? array indices)
    +      (slib:error 'array-ref 'bad-indices indices))
    +  ((if (string? store) string-ref vector-ref)
    +   store (apply + (array:offset array) (map * (array:scales array) indices))))
    +
    +;;@args array obj k1 @dots{}
    +;;Stores @2 in the (@3, @dots{}) element of @1.  The value returned
    +;;by @0 is unspecified.
    +(define (array-set! array obj . indices)
    +  (define store (array:store array))
    +  (or (array:in-bounds? array indices)
    +      (slib:error 'array-set! 'bad-indices indices))
    +  ((if (string? store) string-set! vector-set!)
    +   store (apply + (array:offset array) (map * (array:scales array) indices))
    +   obj))
    +
    +;;@noindent
    +;;These functions return a prototypical uniform-array enclosing the
    +;;optional argument (which must be of the correct type).  If the
    +;;uniform-array type is supported by the implementation, then it is
    +;;returned; defaulting to the next larger precision type; resorting
    +;;finally to vector.
    +
    +(define (make-prototype-checker name pred? creator)
    +  (lambda args
    +    (case (length args)
    +      ((1) (if (pred? (car args))
    +               (creator (car args))
    +               (slib:error name 'incompatible 'type (car args))))
    +      ((0) (creator))
    +      (else (slib:error name 'wrong 'number 'of 'args args)))))
    +
    +(define (integer-bytes?? n)
    +  (lambda (obj)
    +    (and (integer? obj)
    +         (exact? obj)
    +         (or (negative? n) (not (negative? obj)))
    +         (do ((num obj (quotient num 256))
    +              (n (+ -1 (abs n)) (+ -1 n)))
    +             ((or (zero? num) (negative? n))
    +              (zero? num))))))
    +
    +;;@args z
    +;;@args
    +;;Returns an inexact 128.bit flonum complex uniform-array prototype.
    +(define A:floC128b (make-prototype-checker 'A:floC128b complex? vector))
    +;;@args z
    +;;@args
    +;;Returns an inexact 64.bit flonum complex uniform-array prototype.
    +(define A:floC64b (make-prototype-checker 'A:floC64b complex? vector))
    +;;@args z
    +;;@args
    +;;Returns an inexact 32.bit flonum complex uniform-array prototype.
    +(define A:floC32b (make-prototype-checker 'A:floC32b complex? vector))
    +;;@args z
    +;;@args
    +;;Returns an inexact 16.bit flonum complex uniform-array prototype.
    +(define A:floC16b (make-prototype-checker 'A:floC16b complex? vector))
    +
    +;;@args z
    +;;@args
    +;;Returns an inexact 128.bit flonum real uniform-array prototype.
    +(define A:floR128b (make-prototype-checker 'A:floR128b real? vector))
    +;;@args z
    +;;@args
    +;;Returns an inexact 64.bit flonum real uniform-array prototype.
    +(define A:floR64b (make-prototype-checker 'A:floR64b real? vector))
    +;;@args z
    +;;@args
    +;;Returns an inexact 32.bit flonum real uniform-array prototype.
    +(define A:floR32b (make-prototype-checker 'A:floR32b real? vector))
    +;;@args z
    +;;@args
    +;;Returns an inexact 16.bit flonum real uniform-array prototype.
    +(define A:floR16b (make-prototype-checker 'A:floR16b real? vector))
    +
    +;;@args z
    +;;@args
    +;;Returns an exact 128.bit decimal flonum rational uniform-array prototype.
    +(define A:floR128b (make-prototype-checker 'A:floR128b real? vector))
    +;;@args z
    +;;@args
    +;;Returns an exact 64.bit decimal flonum rational uniform-array prototype.
    +(define A:floR64b (make-prototype-checker 'A:floR64b real? vector))
    +;;@args z
    +;;@args
    +;;Returns an exact 32.bit decimal flonum rational uniform-array prototype.
    +(define A:floR32b (make-prototype-checker 'A:floR32b real? vector))
    +
    +;;@args n
    +;;@args
    +;;Returns an exact binary fixnum uniform-array prototype with at least
    +;;64 bits of precision.
    +(define A:fixZ64b (make-prototype-checker 'A:fixZ64b (integer-bytes?? -8) vector))
    +;;@args n
    +;;@args
    +;;Returns an exact binary fixnum uniform-array prototype with at least
    +;;32 bits of precision.
    +(define A:fixZ32b (make-prototype-checker 'A:fixZ32b (integer-bytes?? -4) vector))
    +;;@args n
    +;;@args
    +;;Returns an exact binary fixnum uniform-array prototype with at least
    +;;16 bits of precision.
    +(define A:fixZ16b (make-prototype-checker 'A:fixZ16b (integer-bytes?? -2) vector))
    +;;@args n
    +;;@args
    +;;Returns an exact binary fixnum uniform-array prototype with at least
    +;;8 bits of precision.
    +(define A:fixZ8b (make-prototype-checker 'A:fixZ8b (integer-bytes?? -1) vector))
    +
    +;;@args k
    +;;@args
    +;;Returns an exact non-negative binary fixnum uniform-array prototype with at
    +;;least 64 bits of precision.
    +(define A:fixN64b (make-prototype-checker 'A:fixN64b (integer-bytes?? 8) vector))
    +;;@args k
    +;;@args
    +;;Returns an exact non-negative binary fixnum uniform-array prototype with at
    +;;least 32 bits of precision.
    +(define A:fixN32b (make-prototype-checker 'A:fixN32b (integer-bytes?? 4) vector))
    +;;@args k
    +;;@args
    +;;Returns an exact non-negative binary fixnum uniform-array prototype with at
    +;;least 16 bits of precision.
    +(define A:fixN16b (make-prototype-checker 'A:fixN16b (integer-bytes?? 2) vector))
    +;;@args k
    +;;@args
    +;;Returns an exact non-negative binary fixnum uniform-array prototype with at
    +;;least 8 bits of precision.
    +(define A:fixN8b (make-prototype-checker 'A:fixN8b (integer-bytes?? 1) vector))
    +
    +;;@args bool
    +;;@args
    +;;Returns a boolean uniform-array prototype.
    +(define A:bool (make-prototype-checker 'A:bool boolean? vector))
    +
    + +

    Copyright

    +

    Copyright (C) 2005 Aubrey Jaffer

    + +

    +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: +

    +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. +

    +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +

    +


    +
    Editor: David Van Horn
    + + +Last modified: Thu Jan 27 09:30:33 EST 2005 + + + diff --git a/doc/srfi-std/srfi-64.html b/doc/srfi-std/srfi-64.html new file mode 100644 index 0000000000..db939d6ff1 --- /dev/null +++ b/doc/srfi-std/srfi-64.html @@ -0,0 +1,845 @@ + +SRFI 64: A Scheme API for test suites + + + + +
    +

    Title

    +A Scheme API for test suites +
    + +

    Author

    +

    Per Bothner +<per@bothner.com>

    + +

    Status

    + +This SRFI is currently in ``final'' status. To see an explanation of each +status that a SRFI can hold, see +here. +It will remain in draft status until 2005/03/17, or as amended. To +provide input on this SRFI, please +mailto:srfi minus 64 at srfi dot schemers dot org. +See instructions +here to subscribe to the list. You can access previous messages via +the +archive of the mailing list. +

    +

    +

    + + +

    Abstract

    +

    +This defines an API for writing test suites, to make it easy +to portably test Scheme APIs, libraries, applications, and implementations. +A test suite is a collection of test cases that execute +in the context of a test-runner. This specifications +also supports writing new test-runners, to allow customization +of reporting and processing the result of running test suites.

    + +

    Rationale

    + +

    The Scheme community needs a standard for writing test suites. +Every SRFI or other library should come with a test suite. +Such a test suite must be portable, without requiring any +non-standard features, such as modules. The test suite implementation +or "runner" need not be portable, but it is desirable that it be +possible to write a portable basic implementation.

    +

    +There are other testing frameworks written in Scheme, including +SchemeUnit. +However SchemeUnit is not portable. +It is also a bit on the verbose side. +It would be useful to have a bridge between this framework and SchemeUnit +so SchemeUnit tests could run under this framework and vice versa. +There exists also at least one Scheme wrapper providing a Scheme interface +to the standard JUnit API for Java. +It would be useful to have a bridge so that tests written using this +framework can run under a JUnit runner. +Neither of these features are part of this specification.

    +

    +This API makes use of implicit dynamic state, including an +implicit test runner. This makes the API convenient +and terse to use, but it may be a little less elegant and compositional +than using explicit test objects, such as JUnit-style frameworks. +It is not claimed to follow either object-oriented or functional design +principles, but I hope it is useful and convenient to use and extend.

    +

    +This proposal allows converting a Scheme source file to a +test suite by just adding a few macros. You don't have to +write the entire file in a new form, thus you don't have to +re-indent it.

    +

    +All names defined by the API start with the prefix test-. +All function-like forms are defined as syntax. They may be implemented +as functions or macros or built-ins. The reason for specifying them as +syntax is to allow specific tests to be skipped without evaluating sub-expressions, or for implementations +to add features such as printing line numbers or catching exceptions.

    + +

    Specification

    + +

    While this is a moderately complex specification, +you should be able to write simple test suites after just reading the +first few sections below. More advanced functionality, such +as writing a custom test-runner, is at the end of the specification.

    + +

    Writing basic test suites

    +

    Let's start with a simple example. +This is a complete self-contained test-suite.

    + +
    ;; Initialize and give a name to a simple testsuite.
    +(test-begin "vec-test")
    +(define v (make-vector 5 99))
    +;; Require that an expression evaluate to true.
    +(test-assert (vector? v))
    +;; Test that an expression is eqv? to some other expression.
    +(test-eqv 99 (vector-ref v 2))
    +(vector-set! v 2 7)
    +(test-eqv 7 (vector-ref v 2))
    +;; Finish the testsuite, and report results.
    +(test-end "vec-test")
    +
    +

    +This testsuite could be saved in its own source file. +Nothing else is needed: +We do not require any top-level forms, so it is easy +to wrap an existing program or test to this form, without adding indentation. +It is also easy to add new tests, without having to name individual +tests (though that is optional).

    +

    +Test cases are executed in the context of a test runner, +which is a object that accumulates and reports test results. +This specification defines how to create and use custom test runners, +but implementations should also provide a default test runner. +It is suggested (but not required) that loading the above +file in a top-level environment will cause the +tests to be executed using an implementation-specified default test runner, +and test-end will cause a summary to be displayed +in an implementation-specified manner.

    + +

    Simple test-cases

    +

    +Primitive test cases test that a given condition is true. +They may have a name. +The core test case form is test-assert:

    +
    (test-assert [test-name] expression)
    +
    +

    +This evaluates the expression. +The test passes if the result +is true; if the result is false, a test failure is reported. +The test also fails if an exception is raised, assuming the implementation +has a way to catch exceptions. +How the failure is reported depends on the test runner environment. +The test-name is a string that names the test case. +(Though the test-name is a string literal in the examples, +it is an expression. It is evaluated only once.) +It is used when reporting errors, and also when skipping tests, +as described below. +It is an error to invoke test-assert +if there is no current test runner.

    +

    +The following forms may be more convenient than +using test-assert directly:

    +
    (test-eqv [test-name] expected test-expr)
    +
    +

    +This is equivalent to:

    +
    (test-assert [test-name] (eqv? expected test-expr))
    +
    +

    +Similarly test-equal and test-eq +are shorthand for test-assert combined with +equal? or eq?, respectively:

    +
    (test-equal [test-name] expected test-expr)
    +(test-eq [test-name] expected test-expr)
    +

    +Here is a simple example:

    +
    (define (mean x y) (/ (+ x y) 2.0))
    +(test-eqv 4 (mean 3 5))
    +
    +

    For testing approximate equality of inexact reals +we can use test-approximate:

    +
    (test-approximate [test-name] expected test-expr error)
    +
    +

    +This is equivalent to (except that each argument is only evaluated once):

    +
    (test-assert [test-name]
    +  (and (>= test-expr (- expected error))
    +       (<= test-expr (+ expected error))))
    +
    + +

    Tests for catching errors

    +

    +We need a way to specify that evaluation should fail. +This verifies that errors are detected when required.

    +
    (test-error [[test-name] error-type] test-expr)
    +
    +

    +Evaluating test-expr is expected to signal an error. +The kind of error is indicated by error-type.

    +

    +If the error-type is left out, or it is +#t, it means "some kind of unspecified error should be signaled". +For example:

    +
    (test-error #t (vector-ref '#(1 2) 9))
    +
    +

    +This specification leaves it implementation-defined (or for a future +specification) what form test-error may take, +though all implementations must allow #t. +Some implementations may support +SRFI-35's conditions, +but these are only standardized for +SRFI-36's I/O conditions, which are seldom useful in test suites. +An implementation may also allow implementation-specific +exception types. +For example Java-based implementations may allow +the names of Java exception classes:

    +
    ;; Kawa-specific example
    +(test-error <java.lang.IndexOutOfBoundsException> (vector-ref '#(1 2) 9))
    +
    +

    +An implementation that cannot catch exceptions should skip +test-error forms.

    + +

    Testing syntax

    +

    +Testing syntax is tricky, especially if we want to +check that invalid syntax is causes an error. +The following utility function can help:

    +
    (test-read-eval-string string)
    +
    +

    +This function parses string (using read) +and evaluates the result. +The result of evaluation is returned from test-read-eval-string. +An error is signalled if there are unread characters after the +read is done. +For example:
    +(test-read-eval-string "(+ 3 4)") evaluates to 7.
    +(test-read-eval-string "(+ 3 4") signals an error.
    +(test-read-eval-string "(+ 3 4) ") signals an error, +because there is extra junk (i.e. a space) after the +list is read. +

    +

    +The test-read-eval-string used in tests:

    +
    (test-equal 7 (test-read-eval-string "(+ 3 4)"))
    +(test-error (test-read-eval-string "(+ 3"))
    +(test-equal #\newline (test-read-eval-string "#\\newline"))
    +(test-error (test-read-eval-string "#\\newlin"))
    +
    +;; Skip the next 2 tests unless srfi-62 is available.
    +(test-skip (cond-expand (srfi-62 0) (else 2)))
    +(test-equal 5 (test-read-eval-string "(+ 1 #;(* 2 3) 4)"))
    +(test-equal '(x z) (test-read-string "(list 'x #;'y 'z)"))
    +
    + +

    Test groups and paths

    +

    A test group is a named sequence of forms containing testcases, +expressions, and definitions. +Entering a group sets the test group name; leaving a +group restores the previous group name. +These are dynamic (run-time) operations, and a group has no +other effect or identity. +Test groups are informal groupings: they are neither +Scheme values, nor are they syntactic forms.

    + +

    +A test group may contain nested inner test groups. +The test group path is a list of the currently-active +(entered) test group names, oldest (outermost) first.

    +
    (test-begin suite-name [count])
    +
    +

    A test-begin enters a new test group. +The suite-name becomes the current test group name, +and is added to the end of the test group path. +Portable test suites should use a sting literal for suite-name; +the effect of expressions or other kinds of literals is unspecified.

    +

    +Rationale: In some ways using symbols would be preferable. +However, we want human-readable names, and standard Scheme does not +provide a way to include spaces or mixed-case text in +literal symbols.

    +

    +The optional count must match the number of +test-cases executed by this group. +(Nested test groups count as a single test case for this count.) +This extra test may be useful to catch cases where a test doesn't +get executed because of some unexpected error.

    +

    +Additionally, if there is no currently executing test runner, +one is installed in an implementation-defined manner.

    +
    (test-end [suite-name])
    +
    +

    +A test-end leaves the current test group. +An error is reported if the suite-name does not +match the current test group name. +

    +

    +Additionally, if the matching test-begin +installed a new test-runner, then the test-end +will de-install it, after reporting the accumulated test +results in an implementation-defined manner.

    + +
    (test-group suite-name decl-or-expr ...)
    +
    +

    +Equivalent to:

    +
    (if (not (test-to-skip% suite-name))
    +  (dynamic-wind
    +    (lambda () (test-begin suite-name))
    +    (lambda () decl-or-expr ...)
    +    (lambda () (test-end suite-name))))
    +
    +

    This is usually equivalent to executing the decl-or-exprs +within the named test group. However, the entire group is skipped +if it matched an active test-skip (see later). +Also, the test-end is executed in case of an exception.

    + +

    Handling set-up and cleanup

    +
    (test-group-with-cleanup suite-name
    +  decl-or-expr ...
    +  cleanup-form)
    +
    +

    +Execute each of the decl-or-expr forms in order +(as in a <body>), +and then execute the cleanup-form. +The latter should be executed even if +one of a decl-or-expr forms raises an exception +(assuming the implementation has a way to catch exceptions).

    +

    For example:

    +
    (test-group-with-cleanup "test-file"
    +  (define f (open-output-file "log"))
    +  (do-a-bunch-of-tests f)
    +  (close-output-port f))
    +
    + + + +

    Conditonal test-suites and other advanced features

    +

    +The following describes features for controlling which tests to execute, +or specifing that some tests are expected to fail.

    + +

    Test specifiers

    +

    Sometimes we want to only run certain tests, or we know that +certain tests are expected to fail. +A test specifier is one-argument function that takes a test-runner +and returns a boolean. The specifier may be run before a test is performed, +and the result may control whether the test is executed. +For convenience, a specifier may also be a non-procedure value, +which is coerced to a specifier procedure, as described below for +count and name.

    +

    +A simple example is:

    +
    (if some-condition
    +  (test-skip 2)) ;; skip next 2 tests
    +
    +

    +(test-match-name name)
    +The resulting specifier matches if the current test name (as +returned by test-runner-test-name) is equals? to +name.

    +

    +(test-match-nth n [count])
    +This evaluates to a stateful predicate: A counter keeps track of +how many times it has been called. +The predicate matches the n'th time it is called +(where 1 is the first time), and +the next (- count 1) times, +where count defaults to 1.

    +

    +(test-match-any specifier ...)
    +The resulting specifier matches if any specifier +matches. +Each specifier is applied, in order, +so side-effects from a later specifier happen +even if an earlier specifier is true.

    +

    +(test-match-all specifier ...)
    +The resulting specifier matches if each specifier +matches. +Each specifier is applied, in order, +so side-effects from a later specifier happen +even if an earlier specifier is false.

    +

    +count (i.e. an integer)
    +Convenience short-hand for: (test-match-nth 1 count).

    +

    +name (i.e. a string)
    +Convenience short-hand for (test-match-name name).

    + +

    Skipping selected tests

    +

    In some cases you may want to skip a test.

    +
    (test-skip specifier)
    +
    +

    Evaluating test-skip adds the +resulting specifier +to the set of currently active skip-specifiers. +Before each test (or test-group) +the set of active skip-specifiers are applied to the active test-runner. +If any specifier matches, then the test is skipped.

    +

    +For convenience, if the specifier is a string that +is syntactic sugar for (test-match-name specifier). +For example:

    +
    (test-skip "test-b")
    +(test-assert "test-a")   ;; executed
    +(test-assert "test-b")   ;; skipped
    +
    +

    +Any skip specifiers introduced by a test-skip +are removed by a following non-nested test-end.

    +
    (test-begin "group1")
    +(test-skip "test-a")
    +(test-assert "test-a")   ;; skipped
    +(test-end "group1")      ;; Undoes the prior test-skip
    +(test-assert "test-a")   ;; executed
    +
    + +

    Expected failures

    +

    +Sometimes you know a test case will fail, but you don't have time +to or can't fix it. Maybe a certain feature only works on certain platforms. +However, you want the test-case to be there +to remind you to fix it. You want to note that +such tests are expected to fail.

    +
    (test-expect-fail specifier)
    +
    +

    +Matching tests (where matching is defined as in test-skip) +are expected to fail. This only affects test reporting, +not test execution. For example:

    +
    (test-expect-fail 2)
    +(test-eqv ...) ;; expected to fail
    +(test-eqv ...) ;; expected to fail
    +(test-eqv ...) ;; expected to pass
    +
    + +

    Test-runner

    +

    +A test-runner is an object that runs a test-suite, +and manages the state. The test group path, and the sets skip and +expected-fail specifiers are part of the test-runner. +A test-runner will also typically accumulate statistics about executed tests, +

    +

    +(test-runner? value)
    +True iff value is a test-runner object.

    +

    +(test-runner-current)
    +(test-runner-current runner)
    +Get or set the current test-runner. +If an implementation supports parameter objects +(as in SRFI-39), +then test-runner-current can be a parameter object. +Alternatively, test-runner-current may be implemented +as a macro or function +that uses a fluid or thread-local variable, or a plain global variable.

    +

    +(test-runner-get)
    +Same as (test-runner-current), buth trows an exception +if there is no current test-runner.

    +

    +(test-runner-simple)
    +Creates a new simple test-runner, that prints errors and a summary +on the standard output port.

    +

    +(test-runner-null)
    +Creates a new test-runner, that does nothing with the test results. +This is mainly meant for extending when writing a custom runner.

    +

    +Implementations may provide other test-runners, perhaps +a (test-runner-gui).

    +

    (test-runner-create)
    +Create a new test-runner. Equivalent to +((test-runner-factory)).

    +

    +(test-runner-factory)
    +(test-runner-factory factory)
    +Get or set the current test-runner factory. +A factory is a zero-argument function that creates a new test-runner. +The default value is test-runner-simple, +but implementations may provide a way to override the default. +As with test-runner-current, this may be a parameter object, +or use a per-thread, fluid, or global variable.

    + +

    Running specific tests with a specified runner

    +

    +(test-apply [runner] specifier ... procedure)
    +Calls procedure with no arguments using the specified +runner as the current test-runner. +If runner is omitted, +then (test-runner-current) is used. +(If there is no current runner, one is created as in test-begin.) +If one or more specifiers are listed then only tests matching +the specifiers are executed. A specifier has the same form +as one used for test-skip. A test is executed +if it matches any of the specifiers in the +test-apply and does not match any +active test-skip specifiers.

    +

    +(test-with-runner runner decl-or-expr ...)
    +Executes each decl-or-expr in order in a context +where the current test-runner is runner.

    + +

    Test results

    +

    Running a test sets various status properties in the current test-runner. +This can be examined by a custom test-runner, +or (more rarely) in a test-suite.

    + +

    Result kind

    +

    Running a test may yield one of the following +status symbols:

    +
    +
    'pass
    The passed, as expected.
    +
    'fail
    The test failed (and was not expected to).
    +
    'xfail
    The test failed and was expected to.
    +
    'xpass
    The test passed, but was expected to fail.
    +
    'skip
    The test was skipped.
    +
    +

    +(test-result-kind [runner])
    +Return one of the above result codes from the most recent tests. +Returns #f if no tests have been run yet. +If we've started on a new test, but don't have a result yet, +then the result kind is 'xfail is the test is expected to fail, +'skip is the test is supposed to be skipped, +or #f otherwise.

    +

    +(test-passed? [runner])
    +True if the value of (test-result-kind [runner]) +is one of 'pass or 'xpass. +This is a convenient shorthand that might be useful +in a test suite to only run certain tests if the previous test passed.

    + +

    Test result properties

    +

    +A test runner also maintains a set of more detailed result properties +associated with the current or most recent test. (I.e. the properties of the +most recent test are available as long as a new test hasn't started.) +Each property has a name (a symbol) and a value (any value). +Some properties are standard or set by the implementation; +implementations can add more.

    +

    +(test-result-ref runner 'pname [default])
    +Returns the property value associated with the pname property name. +If there is no value associated with 'pname +return default, +or #f if default isn't specified.

    +

    +(test-result-set! runner 'pname value)
    +Sets the property value associated with the pname +property name to value. +Usually implementation code should call this function, but it may be +useful for a custom test-runner to add extra properties.

    +

    +(test-result-remove runner 'pname)
    +Remove the property with the name 'pname.

    +

    +(test-result-clear runner)
    +Remove all result properties. +The implementation automatically calls test-result-clear +at the start of a test-assert and similar procedures.

    +

    +(test-result-alist runner)
    +Returns an association list of the current result properties. +It is unspecified if the result shares state with the test-runner. +The result should not be modified, on the other hand the result +may be implicitly modified by future test-result-set! or +test-result-remove calls. +However, A test-result-clear does not modify the returned +alist. Thus you can archive result objects from previous runs.

    + +

    Standard result properties

    +

    +The set of available result properties is implementation-specific. +However, it is suggested that the following might be provided:

    +
    +
    'result-kind
    +
    The result kind, as defined previously. +This is the only mandatory result property.
    +(test-result-kind runner) is equivalent to:
    +(test-result-ref runner 'result-kind) +
    +
    'source-file
    +
    'source-line
    +
    If known, the location of test statements (such as test-assert) +in test suite source code..
    +
    'source-form
    +
    The source form, if meaningful and known.
    +
    'expected-value
    +
    The expected non-error result, if meaningful and known.
    +
    'expected-error
    +
    The error-type +specified in a test-error, if it meaningful and known.
    +
    'actual-value
    +
    The actual non-error result value, if meaningful and known.
    +
    'actual-error
    +
    The error value, if an error was signalled and it is known. +The actual error value is implementation-defined.
    +
    + +

    Writing a new test-runner

    +

    This section specifies how to write a test-runner. +It can be ignored if you just want to write test-cases.

    + + +

    Call-back functions

    +

    +These call-back functions are methods (in the object-oriented sense) +of a test-runner. A method test-runner-on-event +is called by the implementation when event happens.

    +

    +To define (set) the callback function for event use the following expression. +(This is normally done when initializing a test-runner.) +
    +(test-runner-on-event! runner event-function)

    +

    +An event-function takes a test-runner argument, and possibly other arguments, depending on the event.

    +

    +To extract (get) the callback function for event do this:
    +(test-runner-on-event runner)

    +

    +To extract call the callback function for event use the following expression. +(This is normally done by the implementation core.)
    +((test-runner-on-event runner) runner other-args ...)

    +

    +The following call-back hooks are available.

    +

    +(test-runner-on-test-begin runner)
    +(test-runner-on-test-begin! runner on-test-begin-function)
    +(on-test-begin-function runner)
    +The on-test-begin-function is called at the start of an +individual testcase, before the test expression (and expected value) are +evaluated. +

    +

    +(test-runner-on-test-end runner)
    +(test-runner-on-test-end! runner on-test-end-function)
    +(on-test-end-function runner)
    +The on-test-end-function is called at the end of an +individual testcase, when the result of the test is available.

    +

    +(test-runner-on-group-begin runner)
    +(test-runner-on-group-begin! runner on-group-begin-function)
    +(on-group-begin-function runner suite-name count)
    +The on-group-begin-function is called by a test-begin, +including at the start of a test-group. +The suite-name is a Scheme string, +and count is an integer or #f.

    +

    +(test-runner-on-group-end runner)
    +(test-runner-on-group-end! runner on-group-end-function)
    +(on-group-end-function runner)
    +The on-group-end-function is called by a test-end, +including at the end of a test-group.

    +

    +(test-runner-on-bad-count runner)
    +(test-runner-on-bad-count! runner on-bad-count-function)
    +(on-bad-count-function runner actual-count expected-count)
    +Called from test-end (before the on-group-end-function +is called) if an expected-count was specified by the matching +test-begin and the expected-count does not match +the actual-count of tests actually executed or skipped.

    +

    +(test-runner-on-base-end-name runner)
    +(test-runner-on-bad-end-name! runner on-bad-end-name-function)
    +(on-bad-end-name-function runner begin-name end-name)
    +Called from test-end (before the on-group-end-function +is called) if a suite-name was specified, and it did not that the +name in the matching test-begin.

    +

    +(test-runner-on-final runner)
    +(test-runner-on-final! runner on-final-function)
    +(on-final-function runner)
    +The on-final-function takes one parameter (a test-runner) +and typically displays a summary (count) of the tests. +The on-final-function is called after called the +on-group-end-function correspondiong to the outermost +test-end. +The default value is test-on-final-simple which writes +to the standard output port the number of tests of the various kinds. +

    +

    +The default test-runner returned by test-runner-simple +uses the following call-back functions:
    +(test-on-test-begin-simple runner)
    +(test-on-test-end-simple runner)
    +(test-on-group-begin-simple runner suite-name count)
    +(test-on-group-end-simple runner)
    +(test-on-bad-count-simple runner actual-count expected-count)
    +(test-on-bad-end-name-simple runner begin-name end-name)
    +You can call those if you want to write a your own test-runner.

    + +

    Test-runner components

    +

    +The following functions are for accessing the other components of a test-runner. +They would normally only be used to write a new test-runner or +a match-predicate.

    +

    +(test-runner-pass-count runner)
    +Returns the number of tests that passed, and were expected to pass.

    +

    +(test-runner-fail-count runner)
    +Returns the number of tests that failed, but were expected to pass.

    +

    +(test-runner-xpass-count runner)
    +Returns the number of tests that passed, but were expected to fail.

    +

    +(test-runner-xfail-count runner)
    +Returns the number of tests that failed, and were expected to pass.

    +

    +(test-runner-skip-count runner)
    +Returns the number of tests or test groups that were skipped.

    +

    +(test-runner-test-name runner)
    +Returns the name of the current test or test group, as a string. +During execution of test-begin this is the name of the +test group; during the execution of an actual test, this is the name +of the test-case. +If no name was specified, the name is the empty string.

    +

    +(test-runner-group-path runner)
    +A list of names of groups we're nested in, with the outermost group first.

    +

    +(test-runner-group-stack runner)
    +A list of names of groups we're nested in, with the outermost group last. +(This is more efficient than test-runner-group-path, +since it doesn't require any copying.)

    +

    +(test-runner-aux-value runner)
    +(test-runner-aux-value! runner on-test)
    +Get or set the aux-value field of a test-runner. +This field is not used by this API or the test-runner-simple +test-runner, but may be used by custom test-runners to store extra state.

    +

    +(test-runner-reset runner)
    +Resets the state of the runner to its initial state. +

    + +

    Example

    +

    This is an example of a simple custom test-runner. +Loading this program before running a test-suite will install +it as the default test runner.

    +
    (define (my-simple-runner filename)
    +  (let ((runner (test-runner-null))
    +	(port (open-output-file filename))
    +        (num-passed 0)
    +        (num-failed 0))
    +    (test-runner-on-test! runner
    +      (lambda (runner result)
    +        (case (cdr (assq 'result-kind result))
    +          ((pass xpass) (set! num-passed (+ num-passed 1)))
    +          ((fail xfail) (set! num-failed (+ num-failed 1)))
    +          (else #t))))
    +    (test-runner-on-final! runner
    +       (lambda (runner)
    +          (format port "Passing tests: ~d.~%Failing tests: ~d.~%"
    +                  num-passed num-failed)
    +	  (close-output-port port)))
    +    runner))
    +
    +(test-runner-factory
    + (lambda () (my-simple-runner "/tmp/my-test.log")))
    +
    + +

    Implementation

    +

    +The test implementation uses cond-expand +(SRFI-0) +to select different code depending on certain SRFI names (srfi-9, +srfi-34, srfi-35, srfi-39), +or implementations (kawa). +It should otherwise be portable to any R5RS implementation.

    + +

    testing.scm

    + +

    Examples

    +

    Here is srfi-25-test.scm, +based converted from Jussi Piitulainen's +test.scm +for SRFI-25.

    + +

    Test suite

    +

    +Of course we need a test suite for the testing framework itself. +This suite srfi-64-test.scm +was contributed by Donovan Kolbly +<donovan@rscheme.org>.

    + +

    Copyright

    +

    +Copyright (C) Per Bothner (2005, 2006)

    +

    +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions:

    +

    +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software.

    +

    +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

    +
    +
    Author: Per Bothner
    +
    Editor: Francisco Solsona
    +

    + + +Last modified: Sun Jan 28 13:40:18 MET 2007 + +

    + + diff --git a/doc/srfi-std/srfi-66.html b/doc/srfi-std/srfi-66.html new file mode 100644 index 0000000000..7d8fcb30b2 --- /dev/null +++ b/doc/srfi-std/srfi-66.html @@ -0,0 +1,139 @@ + + + + +SRFI 66: Octet Vectors + + + +

    Title

    + +Octet Vectors + +

    Authors

    + +Michael Sperber + +

    Status

    + +This SRFI is currently in ``final'' status. To see an explanation of each +status that a SRFI can hold, see +here. +To +provide input on this SRFI, please +mailto:srfi-66@srfi.schemers.org. +See instructions +here to subscribe to the list. You can access previous messages via +the +archive of the mailing list. +

    +

    + +

    Abstract

    This SRFI defines a set of procedures for creating, accessing, and manipulating uniform vectors of octets.

    Rationale

    A number of applications deal with sequences of octets (often called bytes), most prominently interfaces to C and I/O. Vectors are typically too space-costly and too slow to work well in these circumstance. This justifies having a separate type for octet vectors.

    This SRFI is related to SRFI 4 (Homogeneous numeric vector datatypes), which also provides vectors of octets, and uses names consistent with it. However, the extension described here does not require any extensions to the syntax of the underlying Scheme system, and provides a slightly richer set of primtives.

    Scheme systems implementing both SRFI 4 and this SRFI should use the same type for u8vector and octet vectors as specified here.

    Specification

    Octet vectors are objects of a new type. This type may or may not be disjoint from the type of regular vectors. Its elements must be octets, that is, exact integers in the range [0,255].

    As with vectors, the length of a octet vector is the number of elements it contains. This number is fixed. A valid index into a octet vector is an exact, non-negative integer. The first element of a octet vector has index 0, the last element has an index one less than the length of the vector.

    (u8vector? obj)

    Returns #t if obj is a vector, otherwise returns #f. Analogous to vector?.

    (make-u8vector k fill)

    Returns a newly allocated octet vector of k elements. Each element is initialized to fill. Fill must be an octet. Analogous to make-vector.

    (u8vector octet ...)

    Returns a newly allocated octet vector whose elements contain the given arguments, which must all be octets. Analogous to vector.

    (u8vector->list u8vector)
    (list->u8vector octets)

    u8vector->listreturns a newly allocated list of the elements of u8vector in the same order.Analogous to vector->list.

    List->u8vector returns a newly allocated octet vector whose elements are the elements of list octets, which must all be octets. Analogous to list->vector.

    (u8vector-length u8vector)

    Returns the number of elements in u8vector as an exact integer. Analogous to vector-length.

    (u8vector-ref u8vector k)

    k must be a valid index of u8vector. u8vector-ref returns the contents of element k of u8vector. Analogous to vector-ref.

    (u8vector-set! u8vector k octet)

    k must be a valid index of u8vector. u8vector-set! stores octet in element k of u8vector. The number of return values and the return values are unspecified. However, the number of return values is such that it is accepted by a continuation created by begin. Analogous to vector-set!.

    (u8vector=? u8vector-1 u8vector-2)

    Returns #t if u8vector-1 and u8vector-2 are equal---that is, if they have the same length and equal elements at all valid indices.

    (u8vector-compare u8vector-1 u8vector-2)

    Compares u8vector-1 and u8vector-2 and returns a value consistent with the vector ordering specified in SRFI 67, i.e. -1 if u8vector-1 is smaller than u8vector-2, 0 if they are equal, and 1 if u8vector-1 is greater than u8vector-2. Shorter vectors are always smaller than longer ones, and vectors of equal length are compared lexicographically.

    (u8vector-copy! source source-start target target-start n)

    Copies data from octet vector source to octet vector target. Source-start, target-start, and n must be non-negative exact integers that satisfy

    0 <= source-start <= source-start + n <= (u8vector-length source)

    0 <= target-start <= target-start + n <= (u8vector-length target)

    This copies the octets from source at indices [source-start, source-start + n) to consecutive indices in target starting at target-index.

    This must work even if the memory regions for the source and the target overlap, i.e., the octets at the target location after the copy must be equal to the octets at the source location before the copy.

    The number of return values and the return values are unspecified. However, the number of return values is such that it is accepted by a continuation created by begin. Analogous to vector-ref.

    (u8vector-copy u8vector)

    Returns a newly allocated copy of octet vector u8vector.

    Reference Implementation

    This reference implementation makes use of SRFI 9 (Defining Record Types) and SRFI 23 (Error reporting mechanism) .

    (define-record-type :u8vector
    +  (really-make-u8vector elements)
    +  u8vector?
    +  (elements u8vector-elements))
    +
    +(define (ensure-octet thing)
    +  (if (not (and (integer? thing)
    +		(exact? thing)
    +		(>= thing 0)
    +		(<= thing 255)))
    +      (error "not a octet" thing)))
    +
    +(define (make-u8vector k fill)
    +  (ensure-octet fill)
    +  (really-make-u8vector (make-vector k fill)))
    +
    +(define (list->u8vector octets)
    +  (for-each ensure-octet octets)
    +  (really-make-u8vector (apply vector octets)))
    +
    +(define (u8vector->list octets)
    +  (vector->list (u8vector-elements octets)))
    +  
    +(define (u8vector . octets)
    +  (list->u8vector octets))
    +
    +(define (u8vector-length u8vector)
    +  (vector-length (u8vector-elements u8vector)))
    +
    +(define (u8vector-ref u8vector k)
    +  (vector-ref (u8vector-elements u8vector) k))
    +
    +(define (u8vector-set! u8vector k octet)
    +  (ensure-octet octet)
    +  (vector-set! (u8vector-elements u8vector) k octet))
    +
    +(define (u8vector-copy! source source-start target target-start count)
    +  (if (>= source-start target-start)
    +      (do ((i 0 (+ i 1)))
    +	  ((= i count))
    +        (u8vector-set! target
    +                          (+ target-start i) 
    +                          (u8vector-ref source (+ source-start i))))
    +      (do ((i (- count 1) (- i 1)))
    +	  ((= i -1))
    +        (u8vector-set! target
    +                          (+ target-start i) 
    +                          (u8vector-ref source (+ source-start i))))))
    +
    +(define (u8vector-copy u8vector)
    +  (let* ((size (u8vector-length u8vector))
    +	 (copy (make-u8vector size 0)))
    +    (u8vector-copy! u8vector 0 copy 0 size)
    +    copy))
    +
    +(define (u8vector=? u8vector-1 u8vector-2)
    +  (let ((size (u8vector-length u8vector-1)))
    +    (and (= size (u8vector-length u8vector-2))
    +	 (let loop ((i 0))
    +	   (or (>= i size)
    +	       (and (= (u8vector-ref u8vector-1)
    +		       (u8vector-ref u8vector-2))
    +		    (loop (+ 1 i))))))))
    +
    +(define (u8vector-compare u8vector-1 u8vector-2)
    +  (let ((length-1 (u8vector-length u8vector-1))
    +        (length-2 (u8vector-length u8vector-2)))
    +    (cond
    +      ((< length-1 length-2) -1)
    +      ((> length-1 length-2)  1)
    +      (else
    +       (let loop ((i 0))
    +         (if (= i length-1)
    +             0
    +             (let ((elt-1 (u8vector-ref u8vector-1 i))
    +                   (elt-2 (u8vector-ref u8vector-2 i)))
    +               (cond ((< elt-1 elt-2) -1)
    +                     ((> elt-1 elt-2)  1)
    +                     (else (loop (+ i 1)))))))))))
    +

    References

    • SRFI 4 (Homogeneous numeric vector datatypes)
    • The "Byte Vectors" section of The Incomplete Scheme 48 Reference Manual available from this page.

    Copyright

    +Copyright (C) Michael Sperber (2005). All Rights Reserved. +

    +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: +

    +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. +

    +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +


    +
    Editor: David Van Horn
    + diff --git a/doc/srfi-std/srfi-67/srfi-67-Z-G-D-1.png b/doc/srfi-std/srfi-67/srfi-67-Z-G-D-1.png new file mode 100644 index 0000000000..995a933ee7 Binary files /dev/null and b/doc/srfi-std/srfi-67/srfi-67-Z-G-D-1.png differ diff --git a/doc/srfi-std/srfi-67/srfi-67-Z-G-D-2.png b/doc/srfi-std/srfi-67/srfi-67-Z-G-D-2.png new file mode 100644 index 0000000000..e47ce920a8 Binary files /dev/null and b/doc/srfi-std/srfi-67/srfi-67-Z-G-D-2.png differ diff --git a/doc/srfi-std/srfi-67/srfi-67-Z-G-D-3.png b/doc/srfi-std/srfi-67/srfi-67-Z-G-D-3.png new file mode 100644 index 0000000000..976e557148 Binary files /dev/null and b/doc/srfi-std/srfi-67/srfi-67-Z-G-D-3.png differ diff --git a/doc/srfi-std/srfi-67/srfi-67-Z-G-D-4.png b/doc/srfi-std/srfi-67/srfi-67-Z-G-D-4.png new file mode 100644 index 0000000000..c969404298 Binary files /dev/null and b/doc/srfi-std/srfi-67/srfi-67-Z-G-D-4.png differ diff --git a/doc/srfi-std/srfi-67/srfi-67-Z-G-D-5.png b/doc/srfi-std/srfi-67/srfi-67-Z-G-D-5.png new file mode 100644 index 0000000000..dcff4994dd Binary files /dev/null and b/doc/srfi-std/srfi-67/srfi-67-Z-G-D-5.png differ diff --git a/doc/srfi-std/srfi-67/srfi-67-Z-G-D-6.png b/doc/srfi-std/srfi-67/srfi-67-Z-G-D-6.png new file mode 100644 index 0000000000..dea1599951 Binary files /dev/null and b/doc/srfi-std/srfi-67/srfi-67-Z-G-D-6.png differ diff --git a/doc/srfi-std/srfi-67/srfi-67-Z-G-D-7.png b/doc/srfi-std/srfi-67/srfi-67-Z-G-D-7.png new file mode 100644 index 0000000000..c290afe54c Binary files /dev/null and b/doc/srfi-std/srfi-67/srfi-67-Z-G-D-7.png differ diff --git a/doc/srfi-std/srfi-67/srfi-67-Z-G-D-8.png b/doc/srfi-std/srfi-67/srfi-67-Z-G-D-8.png new file mode 100644 index 0000000000..09f496fda0 Binary files /dev/null and b/doc/srfi-std/srfi-67/srfi-67-Z-G-D-8.png differ diff --git a/doc/srfi-std/srfi-67/srfi-67-Z-S.css b/doc/srfi-std/srfi-67/srfi-67-Z-S.css new file mode 100644 index 0000000000..02a6f88f5a --- /dev/null +++ b/doc/srfi-std/srfi-67/srfi-67-Z-S.css @@ -0,0 +1,193 @@ + + body { + color: black; + /* background-color: #e5e5e5;*/ + background-color: #ffffff; + /*background-color: beige;*/ + margin-top: 2em; + margin-left: 8%; + margin-right: 8%; + } + + h1,h2,h3,h4,h5,h6 { + margin-top: .5em; + } + + .title { + font-size: 200%; + font-weight: normal; + } + + .partheading { + font-size: 100%; + } + + .chapterheading { + font-size: 100%; + } + + .beginsection { + font-size: 110%; + } + + .tiny { + font-size: 40%; + } + + .scriptsize { + font-size: 60%; + } + + .footnotesize { + font-size: 75%; + } + + .small { + font-size: 90%; + } + + .normalsize { + font-size: 100%; + } + + .large { + font-size: 120%; + } + + .largecap { + font-size: 150%; + } + + .largeup { + font-size: 200%; + } + + .huge { + font-size: 300%; + } + + .hugecap { + font-size: 350%; + } + + pre { + margin-left: 2em; + } + + blockquote { + margin-left: 2em; + } + + ol { + list-style-type: decimal; + } + + ol ol { + list-style-type: lower-alpha; + } + + ol ol ol { + list-style-type: lower-roman; + } + + ol ol ol ol { + list-style-type: upper-alpha; + } + + /* + .verbatim { + color: #4d0000; + } + */ + + tt i { + font-family: serif; + } + + .verbatim em { + font-family: serif; + } + + .scheme em { + font-family: serif; + color: black; + } + + .scheme { + color: brown; + } + + .scheme .keyword { + color: #990000; + font-weight: bold; + } + + .scheme .builtin { + color: #990000; + } + + .scheme .variable { + color: navy; + } + + .scheme .global { + color: purple; + } + + .scheme .selfeval { + color: green; + } + + .scheme .comment { + color: teal; + } + + .schemeresponse { + color: green; + } + + .navigation { + color: red; + text-align: right; + font-size: medium; + font-style: italic; + } + + .disable { + /* color: #e5e5e5; */ + color: gray; + } + + .smallcaps { + font-size: 75%; + } + + .smallprint { + color: gray; + font-size: 75%; + text-align: right; + } + + /* + .smallprint hr { + text-align: left; + width: 40%; + } + */ + + .footnoterule { + text-align: left; + width: 40%; + } + + .colophon { + color: gray; + font-size: 80%; + text-align: right; + } + + .colophon a { + color: gray; + } + + \ No newline at end of file diff --git a/doc/srfi-std/srfi-67/srfi-67.html b/doc/srfi-std/srfi-67/srfi-67.html new file mode 100644 index 0000000000..26dd01f9b5 --- /dev/null +++ b/doc/srfi-std/srfi-67/srfi-67.html @@ -0,0 +1,2240 @@ + + + + + +SRFI 67: Compare Procedures + + + + + +

    + + +



    SRFI 67: Compare Procedures

    +

    +
    + +
    + + + + + + + + + + + +
    Sebastian Egner  Jens Axel Søgaard
    sebastian.egner-at-philips.com   jensaxel-at-soegaard.net
    +

    +Other formats are available at srfi.schemers.org: +


    +

    December 3, 2005

    +
    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    + +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    + +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    + +

    Contents

    +

    +

    +    1  Abstract and Rationale
    +

    +

    +    2  Introduction
    +

    +

    +    3  Terminology and Conventions
    +

    +

    +    4  Specification
    +        4.1  Comparing atoms
    +        4.2  Comparing lists and vectors
    +        4.3  Comparing pairs and improper lists
    +        4.4  The default compare procedure
    +        4.5  Constructing compare procedures
    +        4.6  Using compare procedures
    +

    +

    +    5  The theory of compare functions
    +

    +

    +    6  Design Rationale
    +

    +

    +    7  Related work
    +

    +

    +    8  Reference implementation
    +

    +

    +

    +

    +Copyright (c) 2005 Sebastian Egner and Jens Axel Søgaard.

    +

    +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +``Software''), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions:

    +

    +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software.

    +

    +THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +

    +

    +

    +

    +

    + +

    1  Abstract and Rationale

    +

    This SRFI can be seen as an extension of the standard procedures +=, <, char<? etc. of +R5RS -- or even as a replacement. +The primary design aspect in this SRFI is the separation of +representing a total order and using it. +For representing the order, we have chosen for truly 3-way +comparisons. +For using it we provide an extensive set of +operations, each of which accepts a procedure used for comparison. +Since these compare procedures are often optional, +comparing built-in types is as convenient as +R5RS , +sometimes more convenient: +For example, testing if the integer index i lies in the +integer range {0, ..., n - 1} can be written as +(<=/<? 0 i n), implicitly invoking default-compare.

    +

    +As soon as new total orders are required, +the infrastructure provided by this SRFI is far more +convenient and often even more efficient than building +each total order from scratch.

    +

    +Moreover, in case Scheme users and implementors find this +mechanism useful and adopt it, +the benefit of having a uniform interface to total orders +to be used in data structures will manifest itself. +Most concretely, a new sorting procedure in the spirit of +this SRFI would have the interface +(my-sort [ compare ] xs), +using default-compare if the optional compare +was not provided. +Then my-sort could be defined using the entire +infrastructure of this SRFI: +Efficient 2- and 3-way branching, +testing for chains and pairwise inequality, +min/max, and general order statistics.

    +

    +

    + +

    2  Introduction

    +

    This SRFI defines a mechanism for comparing Scheme values +with respect to a total order (aka linear order) [1]. +The mechanism provides operations for: +

    +
      +
    1. comparing objects of the built-in types, +

      +
    2. using a total order in situations that arise in programs, +

      +
    3. facilitating the definition of a new total order. +

      +

    +In the following, these aspects will briefly be illustrated.

    +

    +Traditionally, a total order is represented in Scheme by an +order predicate, like < or char<?. +For the purpose of this SRFI, however, a total order is +represented by a Scheme-procedure comparing its two arguments +and returning either -1, 0, or 1 depending +on whether the first argument is considered smaller, equal, +or greater than the second argument respectively. +Examples of such compare procedures include +(lambda (x y) (sign (- x y))) for comparing real numbers, +but also (lambda (x y) 0) comparing anything. +For most built-in types specified in the +Revised5 Report on the Algorithmic Language Scheme +( +R5RS , [3]) compare procedures are specified in +Sections 4.1, 4.2, and 4.3 of this SRFI. +An axiomatic definition of ``compare procedure'' +is given in Section 5.

    +

    +The primary reason for using 3-valued compare procedures +instead of (2-valued) order predicates is efficiency: +When comparison is computationally expensive, +it is wasteful if two predicates are evaluated +where a single 3-valued comparison would suffice. +This point is discussed in greater detail in Section 6.

    +

    +But dealing directly with 3-valued comparisons in +the application program is inconvenient and obscures intention: +For testing x < y one would have +to write (eqv? (compare x y) -1). +For this reason, an operation <? is supplied which allows +to phrase the same test as (<? compare x y). +This is an example of mapping the three possible outcomes of +a comparison into the two boolean values {#f, #t}. +Since <? takes the total order as an explicit parameter, +a comfortably large arsenal of tests can be made available +for each and every total order (Section 4.6). +This deviates from the approach of +R5RS , in which there are +only five operations ( = , <, >, <, >) -- and for each +total order (real/number, char, char-ci, string, +string-ci) a complete set of these five operation is provided.

    +

    +But still, using <? would be inconvenient if the compare +procedure would have to be supplied explicitly every time. +For this reason, the parameter compare is often made +optional in this SRFI -- and the procedure default-compare is +used whenever no compare procedure is passed explicitly. +Default-compare (Section 4.4) defines +some resonable total order on the built-in types of +R5RS .

    +

    +For the third aspect of this SRFI, defining compare procedures, +special control structures (macros) are +provided (Section 4.5). +These control structures can be used in the definition of +a (potentially recursive) compare procedure. +This is best explained by an extended example.

    +

    +

    + +

    Example

    +

    Assume there is a type length representing physical length. +The type has an accessor procedure meters returning the length +in meters (a real number). +A compare procedure for lengths can then be defined in terms of +real-compare (Section 4.1) as: +

    +  (define (length-compare length1 length2)
    +    (real-compare (meters length1) (meters length2)))
    +

    +Now, (<? length-compare x y) tests if +length x is shorter than length y. +Also, (<=/<? length-compare a x b) tests +if length x lies between length a (incl.) and +length b (excl.) +The expression (min-compare length-compare x y z) +is a shortest of the lengths x, y, and z. +Likewise, (chain<? length-compare x1 x2 x3 x4) test +if the lengths x1 x2 x3 x3 are strictly increasing, +and so on (refer to Section 4.6).

    +

    +Furthermore, assume there is another type box representing a physical box. +The type has procedures width, height, and depth +accessing the dimension (each giving a length). +A compare procedure for boxes, comparing first by width then +by height and then by depth, can be defined using the control +structure refine-compare (Section 4.5) as: +

    +  (define (box-compare box1 box2)
    +    (refine-compare 
    +      (length-compare (width  box1) (width  box2))
    +      (length-compare (height box1) (height box2))
    +      (length-compare (depth  box1) (depth  box2))))
    +

    +This time, (<? box-compare b1 b2) tests if box b1 +is smaller than box b2 -- in the sense of the order defined. +Of course, all the other tests, minimum, maximum etc. are available, too.

    +

    +As a final complication, assume that there is also a type bowl +with accessors radius (a length) and open? (a boolean). +Bowls are to be compared first by whether they are open or closed, +and then by radius. +However, bowls and boxes also need to be compared to each other, +ordered such that a bowl is considered ``smaller'' than a box. +(There are type-test predicates box? and bowl?). +Using the control structure select-compare +(Section 4.5) this can be expressed as: +

    +(define (container-compare c1 c2)
    +  (select-compare c1 c2
    +    (bowl? (boolean-compare (open?  c1) (open?  c2))
    +           (length-compare  (radius c1) (radius c2)))
    +    (box?  (box-compare c1 c2))
    +    (else "neither bowls nor boxes" c1 c2)))
    +

    +This is an example of ``hierarchical extension'' of compare +procedures, as explained in Section 5. +Also note the implicit use of refine-compare in +the bowl?-case.

    +

    +The preceeding example illustrates the main functionality of this SRFI. +For other examples, refer to Section 4.4, +and to the file examples.scm included in the reference +implementation.

    +

    +

    + +

    3  Terminology and Conventions

    +

    A compare procedure is a Scheme-procedure of two +arguments returning an exact integer in { - 1,0,1} +such that the valid input values are ordered according +to some total order. +A compare procedure, together with a set of Scheme values +to which it is applicable, represents a compare function +as defined in Section 5.

    +

    +A comparison is either an expression applying +a compare procedure to two values, or the result of such +an expression.

    +

    +Each operation (macro or procedure) processing the value of +a comparison checks if the value is indeed an exact integer +in the set { - 1,0,1}. +If this is not the case, an error is signalled.

    +

    +Compare procedures expecting certain types of argument +should raise an error in case the arguments are not +of this type. +For most compare procedures specified in this SRFI, +this behavior is required. +A compare procedure compare can be used for +type-checking value x by evaluating +(compare x x), +in case that is desired. +This is useful in procedures like chain<? which +guarantee to check each argument unconditionally.

    +

    +

    +

    +

    + +

    4  Specification

    +

    + +

    4.1  Comparing atoms

    +

    +

    +In this section, compare procedures for most of the atomic +types of +R5RS are defined: +Booleans, characters, strings, symbols, and numbers.

    +

    +As a general naming convention, a procedure named +

    +
    + +type-compare-order +
    +

    +compares two object of the type type with +respect to a total order for which order is +a mnemonic hint (e.g. -ci for case-insensitive). +Of course, -order may be absent if there is +just one order or the order is obvious. +It is an error if a compare procedure accepting objects of a +certain type is called with one or two arguments not of that type.

    +

    +

    +
    procedure:  (boolean-compare bool1 bool2) 
    +Compares two booleans, ordered by #f < #t. +
    Note:   +A non-#f value is not interpreted as a ``true value,'' +but rather an error will be signalled. +

    +
    procedure:  (char-compare char1 char2) 
    + +
    procedure:  (char-compare-ci char1 char2) 
    +Compare characters as char<=? and char-ci<=? respectively. +The suffix -ci means ``case insensitive.'' +
    +
    procedure:  (string-compare string1 string2) 
    + +
    procedure:  (string-compare-ci string1 string2) 
    +Compare strings as string<= and string-ci<=?. +The suffix -ci means ``case insensitive.'' +
    Note:   +Compare-string could be defined as

    +

    +  (define (string-compare string1 string2)
    +    (vector-compare-as-list char-compare 
    +                            string1 string2
    +                            string-length string-ref))
    +
    +

    +
    procedure:  (symbol-compare symbol1 symbol2) 
    +Compares symbols as string<= on the names returned by symbol->string.
    + +
    procedure:  (integer-compare x y) 
    + +
    procedure:  (rational-compare x y) 
    + +
    procedure:  (real-compare x y) 
    + +
    procedure:  (complex-compare x y) 
    + +
    procedure:  (number-compare x y) 
    +Compare two numbers. +It is an error if an argument is not of the type specified +by the name of the procedure.

    +Complex numbers are ordered lexicographically on pairs (re, im). +For objects representing real numbers sign(x - y) is computed. +The ordering for values satisfying real? or complex? +but not representing a real or complex number should be consistent with +procedures = and < of +R5RS , +and apart from that it is unspecified.

    +

    +Numerical compare procedures are compatible with the +R5RS +numerical tower in the following sense: +If S is a subtype of the numerical type T +and x, y can be represented both in S and in T, +then compare-S and compare-T compute the same result. +

    +
    Note:   +Floating point formats usually include several symbolic values not +simply representing rational numbers. +For example, the IEEE 754 standard defines -0, -Inf, +Inf, +and NaN ("not a number") for continuing a calculation in the presence +of error conditions. +The behavior of the numerical comparison operation is unspecified +in case an argument is one of the special symbols. +
    Warning: +The propagation of inexactness can lead to surprises. +In a Scheme system propagating inexactness in +complex numbers (such as PLT, version 208):

    +

    +     (complex-compare (make-rectangular (/ 1 3)  1.)
    +                      (make-rectangular (/ 1 3) -1)) 
    +  ===> -1
    +
    At first glance, one might expect the first complex number to be +larger, because the numbers are equal on their real parts and the +first imaginary part (1.) is larger than the second (-1). +Closer inspection reveals that the decimal dot causes the first +real part to be made inexact upon construction of the complex number, +and since (exact->inexact (/ 1 3)) is less than (/ 1 3) +in the underlying floating point format used, +the real parts decide the comparison of the complex numbers. + +
    + + +

    4.2  Comparing lists and vectors

    +

    +

    +In this section compare procedures are defined for Scheme +lists and vectors -- and for objects that can be accessed +like lists or like vectors.

    +

    +An object x can be accessed like a vector if +there are procedures size and ref such that +(size x) is a non-negative integer n +indicating the number of elements, and (ref x i) +is the i-th element of x for i [srfi-67-Z-G-D-4.png] {0, ..., n - 1}. +The default vector access procedures are vector-length +and vector-ref.

    +

    +An object x can be accessed like a (proper) list +if there are procedures empty?, head, and tail +such that (empty? x) is a boolean indicating that +there are no elements in x, (head x) is the +first element of x, and (tail x) is an object +representing the residual elements of x. +The default list access procedures are null?, +car, and cdr.

    +

    +Independent of the way the elements are accessed, +the natural ordering of vectors and lists differs: +Sequences are compared as vectors if +shorter sequences are smaller than longer sequences, +and sequences of the same size are compared lexicographically. +Sequences are compared as lists if the empty +sequence is smallest, and two non-empty sequences are +compared by their first elements, and only if the first +elements are equal the residual sequences are compared, +recursively. +

    +
    procedure:  (vector-compare [ compare ] x y [ size ref ]) 
    + +
    procedure:  (vector-compare-as-list [ compare ] x y [ size ref ]) 
    + +
    procedure:  (list-compare [ compare ] x y [ empty? head tail ]) 
    + +
    procedure:  (list-compare-as-vector [ compare ] x y [ empty? head tail ]) 
    + +Compare two sequences x and y, +using compare for comparing elements. +The result is an exact integer in { - 1, 0, 1}. +If compare is not supplied, default-compare is used.

    +The procedure named access-compare-as-order +accesses the objects like access and compares them with +respect to the order given by order. +The names type-compare are abbreviations for +type-compare-as-type.

    +

    +Examples:

    +

    +

    +  (list-compare           '(2) '(1 2))    ===>  1
    +  (list-compare-as-vector '(2) '(1 2))    ===> -1
    +  (vector-compare         '#(2) '#(1 2))  ===> -1
    +  (vector-compare-as-list '#(2) '#(1 2))  ===>  1
    +
    +
    +

    +

    + +

    4.3  Comparing pairs and improper lists

    +

    +

    +In this section, compare procedures for Scheme +pairs and (possibly) improper lists are defined.

    +

    +

    +
    procedure:  (pair-compare-car compare) 
    + +
    procedure:  (pair-compare-cdr compare) 
    +Construct a compare procedure on pairs which only uses +the car (only the cdr, respectively), and ignores the other. +One could define

    +

    +  (define (pair-compare-car compare)
    +     (lambda (x y) (compare (car x) (car y))))
    +
    +
    Rationale:   +Pair-compare-car can be used to turn a search data +structure (e.g. a heap) into a dictionary: +Store (key . value) pairs and compare them using the +compare procedure (pair-compare-car compare-key). +

    +
    procedure:  (pair-compare compare-car compare-cdr pair1 pair2) 
    + +
    procedure:  (pair-compare [ compare ] obj1 obj2) 
    +Compares two pairs, or (possibly improper) lists.

    +The 4-ary form compares two pairs pair1 pair2 +by comparing their cars using compare-car, +and if the cars are equal the cdrs are compared +using compare-cdr.

    +

    +The 3-ary form compares two objects by type using the ordering +of types +

    +
    + +null < pair < neither-null-nor-pair. +
    +

    +Two objects of type neither-null-nor-pair are compared +using compare. +Two pairs are compared by using compare on the cars, +and if the cars are equal by recursing on the cdrs.

    +

    +The 2-ary form uses default-compare for compare.

    +

    +

    +   (pair-compare '() 'foo)      ===>  -1
    +   (pair-compare '() '(1 . 2))) ===>  -1
    +   (pair-compare '(1 . 2) 'foo) ===>  -1
    +   (pair-compare 3 4)           ===>  -1
    +
    +
    +

    +

    + +

    4.4  The default compare procedure

    +

    +

    +It is convenient to have a compare procedure readily available +for comparing most built-in types.

    +

    +

    +
    procedure:  (default-compare obj1 obj2) 
    +compares its arguments by type using the ordering +
    + +null < +pair < +boolean < +char < +string < +symbol < +number < +vector < +other +
    +

    +Two objects of the same type type are +compared as type-compare would, +if there is such a procedure. +The type null consists of the empty list '(). +The effect of comparing two other objects or +of comparing cyclic structures (made from lists or vectors) +is unspecified. (Implementations are encouraged to add +comparisons for other built-in types, e.g. records, +regexps, etc.) +

    +
    Rationale:   +Default-compare refines pair-compare by splitting +neither-null-nor-pair. +
    Note:   +Default-compare could be defined as follows +(mind the order of the cases!):

    +

    +   (define (default-compare x y)
    +     (select-compare x y
    +       (null?    0)
    +       (pair?    (default-compare (car x) (car y))
    +                 (default-compare (cdr x) (cdr y)))
    +       (boolean? (boolean-compare x y))
    +       (char?    (char-compare    x y))
    +       (string?  (string-compare  x y))
    +       (symbol?  (symbol-compare  x y))
    +       (number?  (number-compare  x y))
    +       (vector?  (vector-compare default-compare x y))
    +       (else (error "unrecognized types" x y))))
    +

    + +

    4.5  Constructing compare procedures

    +

    +

    +An important goal of this SRFI is to provide a mechanism for defining +new compare procedures as conveniently as possible. The syntactic +extensions defined in this section are the primary utilities for doing +so.

    +

    +

    +
    syntax:  (refine-compare <c1> ...) 
    + +Syntax: The <ci> are expressions.

    +Semantics: The arguments <c1> ...are evaluated from left to +right until a non-zero value is found (which then is the value) +or until there are no more arguments to evaluate (in which case +the value is 0). +It is allowed that there are no arguments at all.

    +

    +

    +
    Note:   +This macro is the preferred way to define a compare procedure +as a refinement (refer to Section 5). Example:

    +

    +(define (compare-rectangle r s)
    +  (refine-compare 
    +    (compare-length (width  r) (width  s))
    +    (compare-length (height r) (height s))))
    +

    +
    syntax:  (select-compare <x1> <x2> +<clause1> ...) 
    + +Syntax: Each <clause>, with the possible exception of the last, is of the form +(<type?> <c1...) +where <type?> is an expression evaluating to a predicate procedure, +and <ci> are expressions evaluating to an exact integer in { - 1,0,1}. +The last <clause> may be an ``else clause'', +which has the form +(else <c1...).

    +Semantics: Select-compare is a conditional for defining +hierarchical extensions and refinements of compare +procedures (refer to Section 5). +It compares the values of <x1> and <x2> by +trying the type tests in order, and applies an implict +refine-compare on the consequences upon a match.

    +

    +In more detail, evaluation proceeds as follows: +First <x1> and <x2> are evaluated in +unspecified order, resulting in values x1 and x2, respectively. +Then the clauses are evaluated one by one, from left to right.

    +

    +For clause (<type?> <c1> ...), +first <type?> is evaluated resulting in a +predicate procedure type? and then the +expressions (type? x1) and (type? x2) +are evaluated and interpreted as booleans. +If both booleans are true then the overall value is +(refine-compare <c1> ...). +If only the first is true the result is -1, +if only the second is true the result is 1, +and if neither is true the next clause is considered. +An else clause is treated as if both tests +where true. +If there are no clauses left, the result is 0.

    +

    +Select-compare evaluates <x1> and <x2> +exactly once, even in the absence of any clauses. +Moreover, each <type?> is evaluated at most once and the +resulting procedure type? is called at most twice.

    +

    +

    +
    Note:   +An example of select-compare is the definition +of default-compare given above. +

    +
    syntax:  (cond-compare <clause1> ...) 
    + Syntax: Each <clause>, with the possible exception of the last, is of the form +((<t1> <t2>) <c1...) +where <t1> and <t2> are expressions evaluating to booleans, +and <ci> are expressions evaluating to an exact integer in { - 1,0,1}. +The last <clause> may be an ``else clause'', +which has the form +(else <c1...).

    +Semantics: Cond-compare is another conditional for defining hierarchical +extensions and refinements of compare procedures +(refer to Section 5).

    +

    +Evaluation proceeds as follows: +The clauses are evaluated one by one, from left to right. +For clause ((<t1> <t2>) <c1> ...), +first <t1> and <t2> are evaluated and the +results are interpreted as boolean values. +If both booleans are true then the overall value is +(refine-compare <c1> ...). +If only the first is true the result is -1, +if only the second is true the result is 1, +and if neither is true the next clause is considered. +An else clause is treated as if both booleans where true. +If there are no clauses left (or there are no clauses +to begin with), the result is 0.

    +

    +Cond-compare evaluates each expression at most once.

    +

    +

    +
    Rationale:   +Cond-compare and select-compare only differ +in the way the type tests are specified. +Both ways are equivalent, and each way is sometimes +more convenient than the other. +

    + +

    4.6  Using compare procedures

    +

    +

    +The facilities defined in this section provide a mechanism for +using a compare procedure (passed as a parameter) in the +different situations arising in applications.

    +

    +

    +
    syntax:  (if3 <c> <less> <equal> <greater>) 
    + Syntax: <c>, <less>, <equal>, and <greater> +are expressions.

    +Semantics: If3 is the 3-way conditional for comparisons. +First <c> is evaluated, resulting in value c. +The value c must be an exact integer in { - 1, 0, 1}, +otherwise an error is signalled. +If c = - 1 then the value of the if3-expression +is obtained by evaluating <less>. +If c = 0 then <equal> is evaluated. +If c = 1 then <greater> is evaluated.

    +

    +

    +
    Note:   +As an example, the following procedure inserts x +into the sorted list s, possibly replacing the +first equivalent element.

    +

    +(define (insert compare x s)
    +  (if (null? s)
    +      (list x)
    +      (if3 (compare x (car s))
    +           (cons x s)
    +           (cons x (cdr s)) ; replace
    +           (cons (car s) (insert compare x (cdr s))))))
    +
    Rationale:   +If3 is the preferred way of branching on the result of +a comparison in case all three branches are different. +

    +
    syntax:  (if=? <c> <consequent> [ <alternate> ]) 
    +
    syntax:  (if<? <c> <consequent> [ <alternate> ]) 
    +
    syntax:  (if>? <c> <consequent> [ <alternate> ]) 
    +
    syntax:  (if<=? <c> <consequent> [ <alternate> ]) 
    +
    syntax:  (if>=? <c> <consequent> [ <alternate> ]) 
    +
    syntax:  (if-not=? <c> <consequent> [ <alternate> ]) 
    +Syntax: <c>, <consequent>, and <alternate> are expressions. +If <alternate> is not provided, (if #f #f) is used.

    +Semantics: These six macros are 2-way conditionals for comparisons. +First <c> is evaluated, resulting in value c. +The value c must be an exact integer in { - 1, 0, 1}, +otherwise an error is signalled. +Then, depending on the value of c and the name of the macro, +either <consequence> or <alternate> is evaluated, +and the resulting value is the value of the conditional expression.

    +

    +The branch is chosen according to the following table: +

    +
    + + + + + + +
    <consequent> <alternate>
    if=? c = 0 c [srfi-67-Z-G-D-4.png] { - 1, 1}
    if<? c = - 1 c [srfi-67-Z-G-D-4.png] {0, 1}
    if>? c = 1 c [srfi-67-Z-G-D-4.png] { - 1, 0}
    if<=? c [srfi-67-Z-G-D-4.png] { - 1, 0} c = 1
    if>=? c [srfi-67-Z-G-D-4.png] {0, 1} c = - 1
    if-not=? c [srfi-67-Z-G-D-4.png] { - 1, 1} c = 0 +
    +

    +

    +
    Note:   +The macros if<=? etc. are the preferred way of 2-way branching based +on the result of a comparison. +

    +
    procedure:  (=? [ compare ] [ x y ]) 
    + +
    procedure:  (<? [ compare ] [ x y ]) 
    + +
    procedure:  (>? [ compare ] [ x y ]) 
    + +
    procedure:  (<=? [ compare ] [ x y ]) 
    + +
    procedure:  (>=? [ compare ] [ x y ]) 
    + +
    procedure:  (not=? [ compare ] [ x y ]) 
    +If the values x and y are given, test if x and y are in the +relation specified by the name of the procedure rel?, with respect to +compare procedure compare; otherwise construct a predicate procedure.

    +

    +

    +In the forms (rel? [ compare ] x y), +the result is a boolean (either #t or #f) +depending on (compare x y) and +the test rel? as specified for if<? etc. +If compare is not supplied, default-compare is used.

    +

    +In the form (rel? [ compare ]), +the predicate procedure +(lambda (x y) (rel? compare x y)) is constructed. +Again, if compare is not supplied, default-compare is used.

    +

    +A few examples for illustration +

    +  (>? "laugh" "LOUD") ===> #t
    +  (<? string-compare-ci "laugh" "LOUD") ===> #t
    +  (define char<=? (<=? char-compare))
    +  (sort-by-less '(1 a "b") (<?)) ===> '("b" a 1)
    +  (sort-by-less '(1 a "b") (>?)) ===> '(1 a "b")
    +

    +

    +Warning: +A common mistake is writing (<=? x y z) +where (<=/<=? x y z) is meant; +this will most likely manifest itself at the time +the expression (x y z) is evaluated. +
    +
    procedure:  (</<? [ compare ] [ x y z ]) 
    + +
    procedure:  (</<=? [ compare ] [ x y z ]) 
    + +
    procedure:  (<=/<? [ compare ] [ x y z ]) 
    + +
    procedure:  (<=/<=? [ compare ] [ x y z ]) 
    + +
    procedure:  (>/>? [ compare ] [ x y z ]) 
    + +
    procedure:  (>/>=? [ compare ] [ x y z ]) 
    + +
    procedure:  (>=/>? [ compare ] [ x y z ]) 
    + +
    procedure:  (>=/>=? [ compare ] [ x y z ]) 
    +Test if x, y, and z form a chain with the two relations +specified by the name of the procedure rel1/rel2?, +with respect to the compare procedure compare.

    +If compare is not provided, default-compare is used. +If x y z are not provided, a predicate +procedure of three arguments is constructed. +The order in which the values are compared is unspecified, +but each value is compared at least once.

    +

    +

    +
    Note:   +(<=/<? real-compare 0 x 1) tests if x is a real number +in the half open interval [0,1).

    +
    procedure:  (chain=? compare x1 ...) 
    + +
    procedure:  (chain<? compare x1 ...) 
    + +
    procedure:  (chain>? compare x1 ...) 
    + +
    procedure:  (chain<=? compare x1 ...) 
    + +
    procedure:  (chain>=? compare x1 ...) 
    +Test if the values x1 ...(zero or more values) form +a chain with respect to the relation specified by the name of +the procedure, and with respect to the compare procedure compare. +The result is a boolean (either #t or #f.) +The order in which the values are compared is unspecified, +but each value is compared at least once (even if there is just +one.)

    +A sequence of values x1, ..., xn forms a chain with respect +to the relation rel? if (rel? compare xi xj) +for all 1 < i < j < n. +In particular, this is the case for n [srfi-67-Z-G-D-4.png] {0,1}.

    +

    +Since the relations = , <, >, <, and > are transitive, +it is sufficient to test (rel? compare xi xi+1) +for 1 < i < n.

    +

    +

    +
    Note:   +The reason every xi participates in at least one comparison +is type-checking: +After testing if the values form a chain, these value may be assumed +to be of the type comparable by compare -- and this holds +irrespectively of the number of values, or whether they form a chain. +

    +
    procedure:  (pairwise-not=? compare x1 ...) 
    +Tests if the values x1 ...(zero or more values) are +pairwise unequal with respect to the compare procedure compare. +The result is a boolean (either #t or #f). +The order in which the values are compared is unspecified, +but each value is compared at least once (even if there is just one).

    +The values x1, ..., xn are pairwise unequal if +(not=? compare xi xj) for all i [srfi-67-Z-G-D-8.png] j. +In particular, this is the case for n [srfi-67-Z-G-D-4.png] {0,1}.

    +

    +Since compare defines a total ordering on the values, +the property can be checked in time O(n log n), and +implementations are required to do this. (For example by +first sorting and then comparing adjacent elements). +

    +
    +
    procedure:  (min-compare compare x1 x2 ...) 
    + +
    procedure:  (max-compare compare x1 x2 ...) 
    +A minimum or maximum of the values x1 x2 ...(one or more values) with respect to the compare procedure compare.

    +The result is the first value that is minimal (maximal, respectively). +The order in which the values are compared is unspecified, +but each value is compared at least once (even if there is +just one value). +

    +
    +
    procedure:  (kth-largest compare k x0 x1 ...) 
    +The k-th largest element of values +x0 x1 ...(one or more values) +with respect to the compare procedure compare.

    +More precisely, +(kth-largest compare k x0 ... xn-1) +returns the (modulo k n)-th element of the unique sequence +obtained by stably sorting (x0 ··· xn-1). +(Recall that a sorting algorithm is stable if it does not +permute items with equal key, i.e. equivalent w.r.t. compare).

    +

    +The argument k is an exact integer, and n > 1. +The order in which the values xi are compared is unspecified, +but each value is compared at least once (even if there is +just one value).

    +

    +

    +
    Note:   +The 0-th largest element is the minimum, +the ( - 1)-st largest element is the maximum. +The median is the (n - 1)/2-th largest element if n is odd, +and the average of the (n/2 - 1)-st and n/2-th largest elements +if n is even. +

    +
    procedure:  (compare-by< lt-pred [ x y ]) 
    + +
    procedure:  (compare-by> gt-pred [ x y ]) 
    + +
    procedure:  (compare-by<= le-pred [ x y ]) 
    + +
    procedure:  (compare-by>= ge-pred [ x y ]) 
    + +
    procedure:  (compare-by=/< eq-pred lt-pred [ x y ]) 
    + +
    procedure:  (compare-by=/> eq-pred gt-pred [ x y ]) 
    + +If optional arguments x and y are present then these +are compared with respect to the total order defined by the +predicate(s) given; the result is in { - 1,0,1}. +If x and y are not present then a procedure comparing +its two arguments using the predicate(s) given is constructed and +returned.

    +The predicate procedures mean the following: +(lt-pred x y) tests if x < y, +le-pred tests for <, +gt-pred for >, +ge-pred for >, +and eq-pred tests if x and y are equivalent. +The result returned by a predicate procedure is interpreted +as a Scheme truth value (i.e. #f is false and non-#f +is true).

    +

    +The purpose of the procedures compare-bypredicate(s) +is to define a compare procedure from an order predicate, +and possibly an additional equivalence predicate. +If an equivalence predicate eq-pred is given, it is called +before the order predicate because the equivalence may be +coarser than the total ordering, and it may also be cheaper.

    +

    +

    +
    Note:   +Char-compare could be defined in terms of char<=? as

    +

    +   (define char-compare (compare-by<= char<=?))
    +
    +

    +
    procedure:  (debug-compare compare) 
    +Constructs a compare procedure equivalent to compare +but with debugging code wrapped around the calls to compare. +The debugging code signals an error if it detects a violation +of the axioms of a compare function. +For this it is assumed that compare has no side-effects.

    +More specifically, (debug-compare compare) evaluates +to a compare procedure compare1 which checks reflexivity, +antisymmetry, and transitivity of compare based on the +arguments on which compare1 is called:

    +

    +The procedure compare1 checks reflexivity on any value +passed to compare, +antisymmetry on any pair of values on which compare is called, +and transitivity on triples where two of the arguments are from +the current call to compare1 and the third is a pseudo-random +selection from the two arguments of the previous call to compare1.

    +

    +

    +
    Rationale:   +The test coverage is partial and determined pseudo-randomly, +but the execution time of compare1 is only a constant factor larger +than the execution time of compare. +

    +

    +

    + +

    5  The theory of compare functions

    +

    +

    +

    +

    +

    +

    +NOTE: This section of the SRFI-document can be read at +srfi.schemers.org/srfi-67/. +It was removed from the +HelpDesk version due to the math.

    +

    +The section contains a theoretical justification +for the concept ``compare function''. +First an axiomatic definition of compare functions is given. +Then it is proved that compare functions are just an +unconventional way of defining total orders on equivalence +classes of elements -- and mathematically that +is all there is to say about compare functions.

    +

    +At this point, a mathematician may wonder why we +introduce compare functions in the first place. +The answer is: Because they are convenient and efficient +for writing programs involving total orders.

    +

    + +

    +

    +

    + +

    6  Design Rationale

    +

    +

    +

    +

    +In this section we present our reasoning behind the design +decisions made for this SRFI. +We would like to be explicit on this because we believe +that design is not about the outcome of decisions but +about the alternatives considered. +The section is organized as a Q&A list.

    +

    +

    + +

    Order predicates (2-way) or 3-way comparisons?

    +

    It is mathematical tradition to specify a total order +in terms of a ``less or equal'' (<) relation. +This usually carries over to programming languages in the +form of a <= predicate procedure.

    +

    +However, there are inherently three possible relations +between two elements x and y with respect to a total order: +x < y, x = y, and x > y. +(With respect to a partial order there is a fourth: +x and y are uncomparable.) +This implies that any mechanism based on 2-valued +operations (be it <, or ( = , <), or other) +has cases in which two expressions must be +evaluated in order to determine the relation between +two elements.

    +

    +In practice, this is a problem if a comparison +is computationally expensive. +Examples of this are implicitly defined orders in which the +order of elements depends on their relative position in some enumeration. +(Think of comparing graphs by isomorphism type.) +In this case, each order predicate is as expensive as a +compare procedure -- implying that a proper 3-way branch +could be twice as fast as cascaded 2-way branches. +Hence, there is a potentially considerable loss in performance, +and it is purely due to the interface for comparisons.

    +

    +The primary disadvantage of bare 3-way comparisons +is that they are less convenient, both in use and +in their definition. +Luckily, this problem can be solved quite satisfactorily using +the syntactic (macro) and procedural abstractions of Scheme +(refer to Sections 4.5 and 4.6).

    +

    +

    + +

    How to represent the three cases?

    +

    We have considered the following alternatives for representing +the three possible results of a comparison: +

    +
      +
    1. the exact integers -1, 0, and 1 (used in this SRFI), +

      +
    2. the sign of an exact immediate integer, +

      +
    3. the sign of any Scheme number satisfying real?, +

      +
    4. three different symbols (e.g. '<, '=, and '>), +

      +
    5. an enumeration type consisting of three elements, and +

      +
    6. a built-in type with self-evaluating special constants +(e.g. #<, #=, and #>). +

      +

    +The representation acts as an internal interface between +programs comparing objects and programs using these comparisons.

    +

    +The advantage of using only three values is that the +representation of each case is uniquely defined. +In particular, this enables the use of case +instead of if, and it ensures portability. +Portability of numbers is problematic in +R5RS due to +underspecification and inexactness.

    +

    +The advantage of using a non-unique (numerical) representation +is that the result of a computation can sometimes immediately be +used in a branch, much like the ``non-#f means true''-convention. +However, with the operations in Section 4.6 +this advantage hardly matters. +Moreover, the ``non-#f means true''-convention is +a major cause of unexpected program behavior itself.

    +

    +The advantage of using { - 1, 0, 1} over using three +symbols is that the integers support additional operations, +for example they can directly be used in index computations. +A particularly useful operation is (* sign (compare x y)) +which inverts the order relation depending on sign +(either - 1 or 1). +In addition, the integers are unique -- once it is known that +comparisons result in integers it is obvious which integers. +A minor consideration is that Scheme systems usually +treat small integers as unboxed values, and that integers +are self-evaluating literals.

    +

    +The advantage of using three symbols is that they can be +chosen to be more descriptive. +For example, it is more instructive to see +(symbol-compare 'foo 'bar) +result in 'greater than in 1. +Unfortunately, there is no obvious choice of name for the +three symbols. +Amoung the choices that make sense are +'less 'equal 'greater, +or 'lt 'eq 'gt, +or '< '= '>. +A disadvantage of using symbols for the three cases is +that Scheme symbols are ordered, too, and this ordering +may differ from the desired ordered for the three cases.

    +

    +Some Scheme implementations provide a mechanism for +defining enumeration types. +For example define-enumerated-type +of Scheme 48 can be used to define a type +comparison consisting of three objects, +say lt, eq, gt. +The enumeration can also (directly) be defined on top of +SRFI 9 (Defining Record Types) [10] +by defining three new record types, each of which +having a single instance. +We regard this approach as preferable over three symbols +because comparison results have their own type, +and a sufficiently advanced compiler could use this +information to eliminate redundant type-checks.

    +

    +One step further in this direction is the following +design alternative we have considered: +Due to the fundamental nature of the type +comparison for programming, +it would be worthwhile integrating it into the +core language of Scheme. +This could take the following form: +There are three self-evaluating constants, +e.g. written #< #= #>, +and these are the only instances of the type +comparison. +The type supports two operations: +comparison? and comparison-compare. +Furthermore, eq?, eqv?, +and equal? need to understand the +comparison values. +In other words, comparison is designed +after boolean. +It is unclear, however, which problem this tight integration +of comparisons into the language is solving.

    +

    +Given this situation, we have chosen for { - 1,0,1}, +while providing facilities for using this conveniently -- in +particular it is hardly ever necessary to deal with +the integers directly.

    +

    +

    + +

    How to order complex numbers?

    +

    Mathematically, no total order of the complex numbers exists +which is compatible with the algebraic or topological structure. +Nevertheless, it is useful for programming purposes to have +some total order of complex numbers readily available.

    +

    +Several total orders on the complex numbers are at least +compatible with the natural ordering of real numbers. +The least surprising of these is lexicographic on (re, im).

    +

    +

    +

    +

    + +

    How to order special floating point symbols?

    +

    Floating point formats often do not only represent rational +numbers but extend this set by special symbols, for example ++Inf, -Inf, NaN (``Not a number''), and -0. +How should these symbols be ordered with respect to the +ordinary numerical values and with respect to each other? +(Refer to the discussion archive starting with +msg00010.)

    +

    +Let us briefly recall the purpose of the special symbols. +The general rationale for introducing special symbols into +a floating point format is for numerical calculations to +continue in the presence of data-dependent errors, +while still retaining some meaningful information +about the result. +The symbols +Inf and -Inf indicate that the calculation +has produced a value exceeding the representable range. +The special symbol -0, indicates that a calculation has +produced a value of unrepresentable small magnitude, +but retains the information that the underflow approached +zero from the negative side (otherwise it would be +0). +This sign information is useful in the presence of branch-cuts. +Finally, NaN indicates that the information about the +value has been lost entirely (example: -Inf + Inf) +NaN avoids raising an exception and allows carrying on +with other parts of the calculation. +It should be noted that several NaNs can exist. +For example in the IEEE 754 standard many bit patterns +represent NaN (whatever the interpretation).

    +

    +As +Inf and -Inf are designed to represent extremal numbers, +their ordering with respect to real numbers is obvious. +For signed zeros, the ordering is also obvious. +However, the notion of two zeros (or even three: -0, 0, and +0) +is incompatible with the arithmetic structure of the real numbers. +Hence, in most situations all zeros should be treated as equal, +even though this can destroy information about results. +But the alternative design may also make sense in certain +situations where the full information carried in a floating +point object is to be retained.

    +

    +For NaN (or even several NaNs) the situation is even +more ambiguous because there is not even a natural order +relation of NaN with the other possible floating point values. +One design alternative is to raise an error if NaN is to +participate in a comparison; the reasoning being ``if the +control flow depends on a NaN you are in trouble anyway''. +An alternative is to define some order by force; the +reasoning being ``if an object satisfies real? +then it can be compared with real-compare.'' +Neither approach is obviously better than the other.

    +

    +Given this situation, we have decided to leave the effect of +using a special floating point value in real-compare +unspecified, in line with the approach of +R5RS . +This approach might change once Scheme itself is more +explicit about floating point representations and +numerical computation.

    +

    +

    + +

    How to define default-compare?

    +

    The purpose of default-compare is providing some +well-defined way of comparing two arbitrary Scheme values. +This can be used in all situations in which the user is +unwilling to define a compare procedure explicitly, +for example because the actual details of the total order +do not really matter.

    +

    +As an example, consider the task of dealing +with sets of sets of integers. +In this case, one could simply use sorted lists without +repetition for representing lists and default-compare +already provides a total order.

    +

    +However, there are limits as to how default-compare can be defined. +For example, default-compare cannot easily be based on a hash +code derived from the pointer representing an object due to the close +dependency with the garbage collection mechanism. +Also, we believe it to be more useful to applications if +default-compare is based on type and structure.

    +

    +Unfortunately, this imposes limits on what can be compared +using default-compare because it is very desireable to +have a portable reference implementation. +In particular, portable ways of dealing with circular structures +are overly costly.

    +

    +Naturally, the question arises how the types should be ordered. +For this question it is useful to understand that +boolean-compare and pair-compare both already +define a total order for all values (at least in priciple). +Hence, default-compare could refine one of them, +but unfortunately not both at the same time (unless +#f and '() are minimum and maximum of the order, +respectively). +Since pair-compare is more frequently used than +boolean-compare we base default-compare +on pair-compare. +The other portably comparable types are ordered by +increasing complexity, which clearly is an arbitrary choice.

    +

    +

    + +

    What is the ``lexicographic order''?

    +

    The lexicographic order is a general way of defining +an ordering for sequences from an ordering of elements:

    +

    +In the lexicographic order, the empty sequence is the smallest +sequence of all, and two non-empty sequences are first compared +by their first element and only if these are equal the residual +sequences are compared, recursively.

    +

    +The lexicographic order has its name from its use in a lexicon: +For example, fun < funloving < jolly.

    +

    +

    + +

    What is the ``natural order'' of lists and vectors?

    +

    By ``natural order'' of an abstract data type we mean a total order +that is defined to match the basic operations operations supported +by the data type.

    +

    +The basic access operations with constant execution time +for Scheme lists are null?, car, and cdr. +These are exactly the operations needed for comparing two +sequences lexicographically.

    +

    +The constant time access operations for Scheme vectors +are vector-length (size) and vector-ref (ref). +Using these operations, the fundamental ordering of vectors +is first comparing by size, +and only if the sizes are equal, +by comparing the elements lexicographically.

    +

    +

    + +

    Why are vectors not ordered lexicographically?

    +

    In this SRFI, lists and strings are ordered +lexicographically (`LEX') by default, e.g. "12" < "2". +The default order of vectors is first by length and then +lexicographically (`LENGTH-LEX'), e.g. #(2) < #(1 2). +Alternatively, vectors could be ordered purely lexicographically, too. +In the extreme, lists, strings, and vectors could even be +ordered lexicographically as sequences without distinguishing +the concrete representation, +implying "12" + = (#\1 #\2) + = #(#\1 #\2).

    +

    +The choice affects vector-compare, default-compare, +and the way orders are interpreted conceptually. +Moreover, this SRFI introduces the terminology ``ordered as lists'' +and ``ordered as vectors'' to refer to the two fundamental +ways of lifting an order to sequences (LEX and LENGTH-LEX). +The choice also has implications for any other SRFI +introducing container data types (e.g. 66 and 74), +in case the author wishes to specify default compare +procedures compatible with this SRFI.

    +

    +Summarizing the discussion, there seem to be three major arguments: +

    +
      +
    1. Conceptually vectors and lists are representations of sequences, +and if there is only one ordering for them it should be LEX. +

      +
    2. LENGTH-LEX is more fundamental and efficient for types +supporting a constant-time `size' operation. +

      +
    3. Conceptually strings are ``vectors of characters'' and +strings are conventionally ordered LEX by default, +so vectors should be ordered LEX as well in order to +minimize the potential for confusion. +

      +

    +(Please refer to the discussion archive for details, in particular +msg00054.)

    +

    +We consider 2. the most important due to its mathematical nature, +followed by 1. because it simplifies the design. +While this controversial, we think that it is preferable +to introduce different orders for different data types, +and not derive every order from a single one for sequences. +Finally, we consider 3. a weak argument because the default +ordering of strings is motivated primarily historically for +ordering written words of (small alphabet) natural languages.

    +

    +Concerning other vector-like data types, such as those +introduced by SRFI 66 and 74, we recommend to define a +default ordering which appears most natural for the type. +These can conveniently be named type-as-ordering. +In cases where the order is of minor importance, +we recommend to be compatible with this SRFI.

    +

    +

    + +

    Why so few higher-order constructions?

    +

    An alternative for the control structures (macros) refine-compare, +select-compare, and cond-compare is a set of +higher-order procedures for constructing compare procedures.

    +

    +We have chosen for control structures instead of higher-order +procedures for simplicity. +This becomes particularly evident when a recursive compare procedure, +e.g. default-compare, is to be defined. +Using select-compare it is possible to define default-compare simply +as a procedure calling itself in some branches (refer to the example in +Section 4.4). +In the higher-order approach, the procedure under construction must also +be able to call itself, with arguments that are application specific. +Expressing this with a flexible higher-order procedure is much more indirect.

    +

    +

    + +

    Why the operations <?, <=? etc.?

    +

    Programs need both 2-way branching and 3-way branching. +For 3-way branching, the conditional if3 +is provided.

    +

    +For 2-way branching, the set { - 1,0,1} of results of +a comparison is mapped onto the set {#f, #t}. +There are eight functions from a 3-set into a 2-set; +all six non-constant functions are provided as =?, +<?, etc.

    +

    +The five monotonic functions can be generalized to +chains of values. +In order to make the compare procedure parameter optional +in the ordinary comparisons, separate operations +(chain<?, chain<=? etc.) are defined for chains. +For the sixth operation (not=?) the generalization +to pairwise unequality is defined as pairwise-not=?. +This operation can be implemented efficiently because the +compare procedure also defines a total order.

    +

    +As chains of length three are still frequently tested in +programs (think of a range check ``0 < i < n''), +and often two different relations are combined, +there are special operations for chains of length three +(</<?, </<=?, etc.)

    +

    +For convenience, the compare procedure argument is +made optional as often as possible. +Unfortunately, this opens up a possibility for mistake: +Writing (<=? x y z) where (<=/<=? x y z) is meant. +Fortunately, the mistake will likely manifest itself at the +time (x y z) is evaluated.

    +

    +

    + +

    Why are <? etc. procedures, not macros?

    +

    The procedures <?, </<?, chain<? etc. +could also have been specified as macros. +This would have the advantage that they could make full use +of ``short evaluation'': A chain of comparisons stops as +soon as one of the comparisons has failed; all remaining +argument expressions and comparisons need not be evaluated. +This is potentially more efficient.

    +

    +The advantage of procedures, on the other hand, is that +in Scheme they are ``first class citizens,'' meaning that +they can be passed as arguments and returned from higher-order +procedures.

    +

    +Taking this approach one step further, one can even require +the compare procedures to check the types of all arguments, +even if the result of the comparison is already known. +This is what Section 6.2.5 of +R5RS calls ``transitive`` +behavior of the predicates =, <, etc. +For example, (< 0 x y) first tests if x is positive, +and only if this is the case (< x y) is tested. +But even if x is not positive it is checked that +y is indeed a real -- otherwise an error is raised. +In ``short evaluation,'' on the contrary, if x is not +positive, y can be an arbitrary Scheme value.

    +

    +Clearly, ``transitive'' tests have an overhead, namely that +they need to execute potentially redundant type checks. +Even worse, as types are only known to the compare procedure +the only way to check the type of a value is to compare it, +maybe with itself (which should result in 0 by definition +of a compare procedure).

    +

    +The advantage of ``transitive'' comparisons is the automatic +insertion of a type assertion. +For example, after (chain<? integer-compare x y z) +has been evaluated, no matter the result, +it is known that x, y, and z are integers. +We consider this advantage sufficiently important to pay the price.

    +

    +

    + +

    Why compare-by< etc.?

    +

    It is often easier to define an order predicate, +and possibly a separate equivalence relation, +than it is to define a compare procedure. +For this case, compare< etc. provide a convenient +and robust way of constructing the associated compare +procedure.

    +

    +As has been learned from writing the reference implementation, +despite the fact that each of these procedures is just a few +lines of trivial code, they miraculously attract bugs.

    +

    +

    + +

    How do I define a compare function from just an equivalence?

    +

    You better don't.

    +

    +A compare function defines a total order on equivalence classes, +and vice versa (refer to Section 5). +Hence, a compare procedure compare can be used to +test equivalence: (=? compare x y).

    +

    +In reverse, one could be tempted to define a ``compare function'' +c from just an equivalence relation ~ as c(x, y) = 0 +if x ~ y and c(x, y) = 1 otherwise. +However, c is not antisymmetric (unless all objects are equivalent, +i.e. c(x,y) = 0 for all x, y) and hence it is not a compare function. +In fact, there is no way at all of avoiding a total order on the equivalence classes.

    +

    +This is also reflected in the fact that there are efficient +(log-time) search data structures based on a total order, +but we know of no efficient (sublinear worst-case) data +structures based solely on an equivalence relation. +The following program takes time and space O(h), +where h is the number of equivalence classes in use:

    +

    +

    +(define (equal->compare equal)
    +  (let ((reps '()) (length-reps 0))
    +    (define (index x)
    +      (let loop ((i (- length-reps 1)) (rs reps))
    +        (if (null? rs)
    +            (let ((i length-reps))
    +              (set! reps (cons x reps))
    +              (set! length-reps (+ length-reps 1))
    +              i)
    +            (if (equal x (car rs))
    +                i
    +                (loop (- i 1) (cdr rs))))))
    +    (lambda (x y)
    +      (integer-compare (index x) (index y)))))
    +

    +If equal is an equivalence predicate (i.e. it is reflexive, +symmetric, and transitive) then (equal->compare equal) +is a compare procedure for the objects comparable by equal. +The total order defined is unspecified (as it depends on call sequence).

    +

    +Note that the equivalence predicate equal could be defined +by using a union-find data structure. +But keep in mind that the equivalence relation represented by equal +must not change while (equal->compare equal) is in use-so the +union-find data structure must be unite classes.

    +

    +

    + +

    How do I switch from +R5RS to this SRFI?

    +

    As it happens, the specification of this SRFI is fully +compatible with the 25 order predicates found in +R5RS . +The easiest way of switching is by defining the +R5RS +operations in terms of this SRFI. +Refer to the file r5rs-to-srfi.scm +for the corresponding Scheme-code.

    +

    +Alternatively, each expression involving a reference to an + +R5RS order predicate can be transformed into an equivalent +expression using the facilities of this SRFI. +Be reminded though that this requires an understanding of +the context of the expression in question, +in particular variable bindings, macro definitions, +and the use of eval.

    +

    +However, if the meaning of an expression may be altered, +it is often possible to increase type safety or simplicity. +Consider for example the following potential replacements +of (and (<= 0 i) (< i n)): +

    +  (and (<=? real-compare 0 i) (<? real-compare i n))
    +  (<=/<? real-compare 0 i n)    ; always compares n
    +  (<=/<? integer-compare 0 i n) ; only integer in
    +  (<=/<? 0 i n)                 ; uses default-compare
    +

    +Only the first alternative is equivalent to the original +expression, but the other alternatives might be useful, too, +depending on the goal.

    +

    +

    + +

    Why be so tight with types?

    +

    Most procedures and macros in this SRFI are required to +signal an error if an argument is not according to the +type specified, in particular comparison values must be +exact integer in { - 1,0,1} at all times. +Alternatively, we could have specified that procedures and +macros accept values as general as makes sense.

    +

    +We believe that being tight on types at this fundamental +level of a language pays off quickly. +In particular, this will simplify debugging. +Moreover, static analysis of a program will recognize +more variables of a known type, which allows for more +unboxed values and tighter compiled code. +(Clearly, at the time of this writing this is speculative.)

    +

    +

    + +

    Is there a performance penalty for this SRFI?

    +

    Yes and no.

    +

    +The focus of the reference implementation is correctness and +portability; performance will very likely suffer due to the +overhead of internal procedure calls and type-checking.

    +

    +But as the word ``SRFI'' suggests, this document is a ``request +for implementation,'' meaning we would love to see this SRFI +being implemented efficiently by the implementation experts of +particular Scheme systems. +In practice, this means that most of the operations defined +here, if not all, are supported natively.

    +

    +In this case, there is no performance penalty for using the +mechanisms of this SRFI -- using this SRFI might even be faster +due to explicit 3-way branching and better typing.

    +

    +

    + +

    Why are there optional leading arguments?

    +

    Some operations have an optional first argument. +This is in contrast to common practice in Scheme to +put optional arguments after mandatory arguments.

    +

    +The leading optional argument is always the argument +compare, representing the total order to be used. +If it is missing default-compare is used.

    +

    +In the cases where we have chosen to make compare +optional it is for the sake of brevity, e.g. in (<? x y) +instead of enforcing (<? default-compare x y). +Although an option introduces potential for confusion +(e.g. (<? x y z) vs. (</<? x y z)), +we consider it an important feature for interactive use +and convenient programming +(e.g. in (do ((i 0 (+ i 1))) ((=? i n)))).

    +

    +Given our decision for optional compare, +the question arises how to pass the option. +In the absence of other widely accepted mechanisms for options, +we can only vary the length of the argument list. +For historical reasons -- before case-lambda of SRFI 16 -- +optional arguments are passed at the end of the argument list +for simplified parsing. +On the other hand, (<? compare x y) is more consistent +with the rest of the SRFI than (<? x y compare).

    +

    +Unfortunately, any particular choice here is a compromise, +and it is also controversial. +(Please refer to the discussion archive for details, in particular +msg00051.) +We have chosen for notational convenience in the common +case (optional compare) and for +consistency within this SRFI (leading optional argument).

    +

    +

    + +

    Why chain<? etc. and not a predicate parameter?

    +

    This SRFI specifies the five chain predicates chain=?, +chain<?, chain>?, chain<=?, and chain>=?. +An alterative is to define a single chain predicate that +has the ordering as a parameter. +(Refer to the discussion archive starting with +msg00012.)

    +

    +The reason we have chosen for five chain predicates is that +we use compare procedures to represent orders, not predicate +procedures. +There are five possible order relations predicates for which +a chain test makes sense. (The sixth, not=?, is not +not transitive and hence requires pairwise testing.) +The five chain tests are clearly defined and can be +implemented efficiently, their main overhead being the +call to the compare procedure.

    +

    +

    + +

    Why not more higher-order procedures?

    +

    In this SRFI min-compare accepts a compare procedure as +a first mandatory argument, applying the minimum operation to +the list of all other arguments. +An alternative is to have min-compare accept only +the compare procedure (possibly optional) and returing a +procedure computing the minimum of all its arguments +(with respect to the compare procedure.) +In a similar fashion other operations can specified as +higher-order procedures.

    +

    +We have avoided higher-order procedures in this SRFI +for simplicity and efficiency. +As said repeatedly, compare procedures are the main +vehicle to transport total orders from the code site +definine an order to the code site using an order. +Moreover, most operations made available through this +SRFI appear rather infrequently in programs, so either +way there is little to be gained. +Finally, dealing with higher-order procedures often +involves writing more parentheses and the more simple-minded +Scheme systems will create many short-lived closures.

    +

    +

    + +

    Why do <? etc. have so many options?

    +

    The procedures =?, <? etc. accept an optional +compare procedure but also two optional arguments to compare. +This could be made simpler by not specifying some of +the cases, or by specifying different procedures for the +different functions.

    +

    +The operations <? etc. are the primary mechanism +for using compare procedures. +As such they should be versatile and concise.

    +

    +Our original design had two mandatory arguments for +objects to compare and an optional argument for the +compare procedure, i.e. it provides a parametric +comparison (<? compare x y) of two objects. +Amir Livne Bar-On then raised the issue of +having better support for a higher-order style of +programming, i.e. ((<? compare) x y). +(Refer to msg00012.)

    +

    +However, in Scheme the higher-order style is +less convenient than it is in curried programming +languages like Haskell or ML. +In practice this manifests itself as follows: +The most basic and frequent case of comparing +atomic objects with respect to the default ordering would +read ((<=?) x y), +which is just two parentheses short of optimal.

    +

    +Fortunately, Dave Mason proposed a syntax for resolving +the apparent alternative parametric test vs. higher order style. +(Refer to +msg00014.) +By combining both functionalities into a single procedure, +the user can choose the style at any moment. +

    + +

    7  Related work

    +

    The use of compare procedures is not new; +defining control structures (if3, select-compare etc.) +for dealing with them efficiently, however, seems to be new +(at least we have not seen it before).

    +

    +Total ordering in +R5RS is represented by typed order +predicates, such as <=, char<=? etc. +Although a ``less or equal''-predicate is sufficient to define +a total order, +R5RS defines a complete set of compare +predicates (that is = , <, >, <, and <) for +the sake of convenience and readability. +There are 25 procedures related to total orders in +R5RS . +These are named +(=|<|>|<=|>=) and +(char|string)[-ci](=|<|>|<=|>=)?.

    +

    +The traditional approach in Scheme to equivalence (``Are two +values treated as equal?'') is the fixed set of predicates +eq?, eqv?, and equal?. +Historically, this approach was motivated by the desire to +compare only pointers and avoid structural recursion. +This SRFI provides the generalization to arbitrary equivalence +relations, provided the equivalence classes are totally ordered.

    +

    +The Ruby programming language [4] provides a method +<=> which is a compare procedure in the sense of this SRFI. +By (re-)defining this method a total order can be defined +for the instances of a class, when compared against other objects. +All 2-way comparisons are based on <=>, +but in Ruby essentially every method can be overloaded.

    +

    +In the Haskell 98 programming language [6] order +predicates and compare functions coexist. +The type Ordering [6, Sect 6.1.8] is an +enumeration of the three symbolic constants +LT, EQ, GT. +The type class Ord [6, Sect 6.3.2] asserts +the presence of a total order for a type, provided +the type class Eq [6, Sect 6.3.1] also +asserts the presence of an equivalence. +Since the default definition of the method compare +is in terms of the methods == and <=, +and vice versa, it can be chosen easily how to provide +the total order without affecting its pattern of use. +

    +

    +The C function strcmp [7] of the ``string.h''-library acts as a compare procedure +in the sense of this SRFI, +although it is specified to return an integer of +which only the sign matters. +Python [5] has a built-in function cmp +which is a compare procedure in the sense of this SRFI.

    +

    +In SRFI-32 (Sort libraries) [13] the total orders +used for sorting are represented by a ``less than'' procedure. +The discussion archive [13] contains a short +discussion thread on the use of 3-value comparisons under +the aspect whether they can be used to improve the sorting +algorithm itself.

    +

    +In the Galore.plt library of data structures for PLT Scheme, +total orders are represented by the signature definition +(define-signature +order^ (elm= elm< elm<=)).

    +

    +

    +

    +

    + +

    8  Reference implementation

    +

    The reference implementation is contained in the +file +compare.scm; +it is implemented in +R5RS +(including hygienic macros) together with +SRFI-16 (case-lambda) [9] +SRFI-23 (error) [11] +SRFI-27 (random-integer) [12].

    +

    +Test code and examples are collected in +examples.scm; +it requires SRFI-42 (comprehensions) [14]. +The reference implementation and the testing code have +been developed and are known to run under +PLT/DrScheme 208p1 [15], +Scheme 48 1.1 [16], and +Chicken 1.70 [17].

    +

    +Code defining the order predicates of +R5RS in terms +of this SRFI is in the file +r5rs-to-srfi.scm. +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    +

    + +

    References

    +

    + + + + + + + + + + + + + + + + + +
    [1]   +E. Weisstein: +Totally Ordered Set,
    +Mathworld at Wolfram Research.
    +TotallyOrderedSet.html +
    [2]   +E. Weisstein: +Equivalence Relation,
    +Mathworld at Wolfram Research.
    +mathworld.wolfram.com/EquivalenceRelation.html +
    [3]   +R. Kelsey, W. Clinger, J. Rees (eds.): +Revised5 Report on the Algorithmic Language Scheme,
    +Higher-Order and Symbolic Computation, Vol. 11, No. 1, August, 1998.
    +www.schemers.org/Documents/Standards/R5RS/ +
    [4]   +Y. Matsumoto: +Programming Ruby. +The Pragmatic Programmer's Guide.
    +www.ruby-doc.org/docs/ProgrammingRuby/ +
    [5]   +G. van Rossum, F. L. Drake, Jr., (ed.): +Python Library Reference. +Release 2.4 of 30 November 2004. +Section 2.1 ``built-in functions''. +Python Software Foundation.
    +http://docs.python.org/lib/lib.html +
    [6]   +S. Peyton Jones (ed.): +Haskell 98 Language and Libraries +The Revised Report, December 2002.
    +http://www.haskell.org/definition/ +
    [7]   +ANSI-C ISO/IEC 9899:1999, published 1 December.
    +http://www.open-std.org/jtc1/sc22/wg14/www/standards +
    [8]   +J. A. Søgaard: +Data Structures Galore for PLT Scheme.
    +http://planet.plt-scheme.org:80/207.1/docs/soegaard/galore.plt/1/1/doc.txt +
    [9]   +L. T. Hansen: +SRFI 16 Syntax for procedures of variable arity.
    +http://srfi.schemers.org/srfi-16/ +
    [10]   +R. Kelsey: +SRFI 9 Defining record types.
    +http://srfi.schemers.org/srfi-9/ +
    [11]   +S. Houben: +SRFI 23 Error reporting mechanism.
    +http://srfi.schemers.org/srfi-23/ +
    [12]   +S. Egner: +SRFI 27 Sources of random bits.
    +http://srfi.schemers.org/srfi-27/ +
    [13]   +O. Shivers: +SRFI 32 Sort libraries. +Section ``Ordering, comparison functions & stability'' +and mail-archive msg000{23,24,33}.html. +SRFI has been withdrawn July 17, 2003.
    +http://srfi.schemers.org/srfi-32/ +
    [14]   +S. Egner: +SRFI 42 Eager comprehensions.
    +http://srfi.schemers.org/srfi-42/ +
    [15]   +PLT Scheme.
    +http://www.plt-scheme.org/ +
    [16]   +R. Kelsey, J. Rees: +Scheme48, version 1.1.
    +http://s48.org/ +
    [17]   +Chicken, version 1.70.
    +www.call-with-current-continuation.org. +

    + + +

    +
     

    +Alphabetic Index

    +

    +

    +

    +

    +

    +

    +

    + +

    +
    +</<=?
    +</<?
    +<=/<=?
    +<=/<?
    +<=?
    +<?
    +=?
    +>/>=?
    +>/>?
    +>=/>=?
    +>=/>?
    +>=?
    +>?

    +

    +
    +

    +

    +

    +

    +
    +boolean-compare

    +

    +
    +

    +

    +

    +

    +
    +chain<=?
    +chain<?
    +chain=?
    +chain>=?
    +chain>?
    +char-compare
    +char-compare-ci
    +compare-by<
    +compare-by<=
    +compare-by=/<
    +compare-by=/>
    +compare-by>
    +compare-by>=
    +complex-compare
    +cond-compare

    +

    +
    +

    +

    +

    +

    +
    +debug-compare
    +default-compare

    +

    +
    +

    +

    +

    +

    +
    +if-not=?
    +if3
    +if<=?
    +if<?
    +if=?
    +if>=?
    +if>?
    +integer-compare

    +

    +
    +

    +

    +

    +

    +
    +kth-largest

    +

    +
    +

    +

    +

    +

    +
    +list-compare
    +list-compare-as-vector

    +

    +
    +

    +

    +

    +

    +
    +max-compare
    +min-compare

    +

    +
    +

    +

    +

    +

    +
    +not=?
    +number-compare

    +

    +
    +

    +

    +

    +

    +
    +pair-compare, [2]
    +pair-compare-car
    +pair-compare-cdr
    +pairwise-not=?

    +

    +
    +

    +

    +

    +

    +
    +rational-compare
    +real-compare
    +refine-compare

    +

    +
    +

    +

    +

    +

    +
    +select-compare
    +string-compare
    +string-compare-ci
    +symbol-compare

    +

    +
    +

    +

    +

    +

    +
    +vector-compare
    +vector-compare-as-list

    +

    +

    + +

    +

    +

    +

    + + + + + + diff --git a/doc/srfi-std/srfi-69.html b/doc/srfi-std/srfi-69.html new file mode 100644 index 0000000000..b93e00c9cd --- /dev/null +++ b/doc/srfi-std/srfi-69.html @@ -0,0 +1,687 @@ + + + + SRFI 69: Basic hash tables + + + + + + + +

    Title

    + +SRFI 69: Basic hash tables + +

    Author

    + +Panu Kalliokoski + +

    Status

    + +This SRFI is currently in ``final'' status. To see an explanation of each +status that a SRFI can hold, see +here. +It will remain in draft status until 2005/09/09, or as amended. To +provide input on this SRFI, please +mailto:srfi-69@srfi.schemers.org. +See instructions +here to subscribe to the list. You can access previous messages via +the +archive of the mailing list. +

    +

    + +

    Abstract

    + +

    This SRFI defines basic hash tables. Hash tables are widely recognised +as a fundamental data structure for a wide variety of applications. A +hash table is a data structure that: + +

    1. provides a mapping from some set of keys to some set of values +associated to those keys +
    2. has no intrinsic order for the (key, value) associations it contains +
    3. supports in-place modification as the primary means of setting the +contents of a hash table +
    4. provides key lookup and destructive update in amortised constant +time, provided that a good hash function is used. + +

    This SRFI aims to accomplish these goals: + +

    1. to provide a consistent, generic and widely applicable API for hash +tables +
    2. to improve code portability by providing a standard hash table +facility with guaranteed behaviour +
    3. to help the programmer by defining utility routines that account for +the most common situations of using hash tables. + +

    Issues

    + +

    There is no single best way to make hash tables. The tables presented +in this SRFI aim at being both conceptually simple and usable for a wide +variety of applications. Even though a portable implementation is +provided, Scheme implementations can speed things up considerably by +e.g. providing an internal hash function for symbols. Moreover, almost +every Scheme implementation already has some kind of low-level hash +table functionality, because that's the natural way to implement +the global environment, and specifically, to provide support for +string->symbol. There might be some benefit in integration +between implementation-specific environment data types and the hash +table API presented here; however, these issues are left open. + +

    This SRFI does not conform to the interface of maps presented in SRFI +44. Following SRFI 44 would seriously cripple the interface of hash +tables. The naming of the operations for maps in SRFI 44 goes against +common use and is unnatural. However, this SRFI has been written so +that it does not prevent a SRFI-44 API to hash tables. An +implementation supporting both SRFI 44 and this SRFI is encouraged to +provide a SRFI 44 interface to hash tables in addition to the one +presented here. + +

    Rationale

    + +

    Hash tables are widely recognised as a fundamental data structure for +many kinds of computational tasks. Thus far, there is no existing +standard for Scheme hash tables; however, almost every non-minimal +Scheme implementation provides some kind of hash table functionality. + +

    Alas, although somewhat similar, these hash table APIs have many +differences: some trivial, like the naming of certain functions; some +complex, like revealing different aspects of the internal implementation +to the user; some coarse, like requiring keys to be of some specific +type(s); some subtle, like requiring the user to guess the size of the +hash table in advance to get optimal performance. As a result, the +existing hash table facilities cannot be used to write portable +programs. + +

    The primary aim of this SRFI is to establish a standard API for hash +tables so that portable programs can be written that make efficient use +of common hash table functionality. The SRFI resolves discrepancies +that exist between the various hash table API's with respect to naming +and semantics of hash table operations. A lot of effort has been put +into making the the API consistent, simple and generic. The SRFI also +defines some of the most common utility routines that would otherwise +need to be written and rewritten for various applications. + +

    Incorporating this SRFI as a standard feature in Scheme implementations +makes it possible to write efficient and portable programs that use hash +tables. + +

    Specification

    + +

    Names defined in this SRFI: + +

    Type constructors and predicate
    +
    make-hash-table, hash-table?, alist->hash-table + +
    Reflective queries
    +
    hash-table-equivalence-function, hash-table-hash-function + +
    Dealing with single elements
    +
    hash-table-ref, hash-table-ref/default, hash-table-set!, +hash-table-delete!, hash-table-exists?, +hash-table-update!, hash-table-update!/default + +
    Dealing with the whole contents
    +
    hash-table-size, hash-table-keys, hash-table-values, +hash-table-walk, hash-table-fold, hash-table->alist, +hash-table-copy, hash-table-merge! + +
    Hashing
    +
    hash, string-hash, string-ci-hash, hash-by-identity + +

    An implementation that does not provide hash-table-ref, +hash-table-set!, hash-table-delete!, hash-table-update!, +hash-table-exists?, and hash-table-size in amortised constant +time (when a good hash function is used), or fails to provide good hash +function definitions for hash, string-hash, string-ci-hash, +and hash-by-identity, does not conform to this SRFI. + +

    Hash table implementations are allowed to rely on the fact that the hash +value of a key in hash table does not change. In most cases, modifying +a key in-place after it has been inserted into the hash table will +violate this constraint and thus leads to unspecified behaviour. + +

    Type constructors and predicate

    + +

    Procedure: make-hash-table [ equal? [ hash [ args … ]]] +→ hash-table + +

    Create a new hash table with no associations. equal? is a +predicate that should accept two keys and return a boolean telling +whether they denote the same key value; it defaults to equal?. + +

    hash is a hash function, and defaults to an appropriate hash function +for the given equal? predicate (see section Hashing). However, +an acceptable default is not guaranteed to be given for any equivalence +predicate coarser than equal?, except for string-ci=?.[1] The function hash must be acceptable for equal?, so if +you use coarser equivalence than equal? other than string-ci=?, +you must always provide the function hash yourself. +
    [1] An +equivalence predicate c1 is coarser than a equivalence predicate c2 +iff there exist values x and y such that (and (c1 x y) (not (c2 x +y))).
    + +

    Implementations are allowed to use the rest args for +implementation-specific extensions. Be warned, though, that using these +extensions will make your program less portable. + +

    Procedure: hash-table? objboolean + +

    A predicate to test whether a given object obj is a hash table. The +hash table type should be disjoint from all other types, if possible. + +

    Procedure: alist->hash-table alist [ equal? [ hash +[ args … ]]] → hash-table + +

    Takes an association list alist and creates a hash table +hash-table which maps the car of every element in alist to the +cdr of corresponding elements in alist. equal?, hash, and +args are interpreted as in make-hash-table. If some key occurs +multiple times in alist, the value in the first association will take +precedence over later ones. (Note: the choice of using cdr (instead +of cadr) for values tries to strike balance between the two +approaches: using cadr would render this procedure unusable for +cdr alists, but not vice versa.) + +

    The rest args are passed to make-hash-table and can thus be used for +implementation-specific extensions. + +

    Reflective queries

    + +

    Procedure: hash-table-equivalence-function hash-table + +

    Returns the equivalence predicate used for keys of hash-table. + +

    Procedure: hash-table-hash-function hash-table + +

    Returns the hash function used for keys of hash-table. + +

    Dealing with single elements

    + +

    Procedure: hash-table-ref hash-table key [ thunk ] → value + +

    This procedure returns the value associated to key in hash-table. +If no value is associated to key and thunk is given, it is called +with no arguments and its value is returned; if thunk is not given, an +error is signalled. +Given a good hash function, this operation should have an (amortised) +complexity of O(1) with respect to the number of associations in +hash-table. (Note: this rules out implementation by association lists +or fixed-length hash tables.) + +

    Procedure: hash-table-ref/default hash-table key default → +value + +

    Evaluates to the same value as (hash-table-ref hash-table key (lambda +() default)). +Given a good hash function, this operation should have an (amortised) +complexity of O(1) with respect to the number of associations in +hash-table. (Note: this rules out implementation by association lists +or fixed-length hash tables.) + +

    Procedure: hash-table-set! hash-table key value → undefined + +

    This procedure sets the value associated to key in hash-table. +The previous association (if any) is removed. +Given a good hash function, this operation should have an (amortised) +complexity of O(1) with respect to the number of associations in +hash-table. (Note: this rules out implementation by association lists +or fixed-length hash tables.) + +

    Procedure: hash-table-delete! hash-table key → undefined + +

    This procedure removes any association to key in hash-table. It is +not an error if no association for that key exists; in this case, +nothing is done. +Given a good hash function, this operation should have an (amortised) +complexity of O(1) with respect to the number of associations in +hash-table. (Note: this rules out implementation by association lists +or fixed-length hash tables.) + +

    Procedure: hash-table-exists? hash-table keyboolean + +

    This predicate tells whether there is any association to key in +hash-table. +Given a good hash function, this operation should have an (amortised) +complexity of O(1) with respect to the number of associations in +hash-table. (Note: this rules out implementation by association lists +or fixed-length hash tables.) + +

    Procedure: hash-table-update! hash-table key function +[ thunk ] → undefined + +

    Semantically equivalent to, but may be implemented more efficiently +than, the following code: +

    +(hash-table-set! hash-table key
    +                 (function (hash-table-ref hash-table key thunk)))
    +
    + +

    Procedure: hash-table-update!/default +hash-table key function default → undefined + +

    Behaves as if it evaluates to (hash-table-update! hash-table key +function (lambda () default)). + +

    Dealing with the whole contents

    + +

    Procedure: hash-table-size hash-tableinteger + +

    Returns the number of associations in hash-table. This operation +must have a complexity of O(1) with respect to the number of +associations in hash-table. + +

    Procedure: hash-table-keys hash-tablelist + +

    Returns a list of keys in hash-table. The order of the keys is +unspecified. + +

    Procedure: hash-table-values hash-tablelist + +

    Returns a list of values in hash-table. The order of the values is +unspecified, and is not guaranteed to match the order of keys in the +result of hash-table-keys. + +

    Procedure: hash-table-walk hash-table proc → unspecified + +

    proc should be a function taking two arguments, a key and a value. +This procedure calls proc for each association in hash-table, giving +the key of the association as key and the value of the association as +value. The results of proc are discarded. The order in which +proc is called for the different associations is unspecified. + +

    (Note: in some implementations, there is a procedure called +hash-table-map which does the same as this procedure. However, in +other implementations, hash-table-map does something else. In no +implementation that I know of, hash-table-map does a real functorial +map that lifts an ordinary function to the domain of hash tables. +Because of these reasons, hash-table-map is left outside this SRFI.) + +

    Procedure: hash-table-fold hash-table f init-value +→ final-value + +

    This procedure calls f for every association in hash-table with +three arguments: the key of the association key, the value of the +association value, and an accumulated value, val. val is +init-value for the first invocation of f, and for subsequent +invocations of f, the return value of the previous invocation of f. +The value final-value returned by hash-table-fold is the return +value of the last invocation of f. The order in which f is called +for different associations is unspecified. + +

    Procedure: hash-table->alist hash-tablealist + +

    Returns an association list such that the car of each element in +alist is a key in hash-table and the corresponding cdr of each +element in alist is the value associated to the key in hash-table. +The order of the elements is unspecified. + +

    The following should always produce a hash table with the same mappings +as a hash table h: +

    +(alist->hash-table (hash-table->alist h)
    +                        (hash-table-equivalence-function h)
    +                        (hash-table-hash-function h))
    +
    + +

    Procedure: hash-table-copy hash-tablehash-table + +

    Returns a new hash table with the same equivalence predicate, hash +function and mappings as in hash-table. + +

    Procedure: hash-table-merge! hash-table1 hash-table2 → +hash-table + +

    Adds all mappings in hash-table2 into hash-table1 and returns the +resulting hash table. This function may modify hash-table1 +destructively. + +

    Hashing

    + +

    Hashing means the act of taking some value and producing a number from +the value. A hash function is a function that does this. Every +equivalence predicate e has a set of acceptable hash functions for +that predicate; a hash funtion hash is acceptable iff (e obj1 +obj2)(= (hash obj1) (hash obj2)). + +

    A hash function h is good for a equivalence predicate e if it +distributes the result numbers (hash values) for non-equal objects (by +e) as uniformly as possible over the numeric range of hash values, +especially in the case when some (non-equal) objects resemble each other +by e.g. having common subsequences. This definition is vague but should +be enough to assert that e.g. a constant function is not a good hash +function. + +

    When the definition of make-hash-table above talks about an +appropriate hashing function for e, it means a hashing function that +gives decent performance (for the hashing operation) while being both +acceptable and good for e. This definition, too, is intentionally +vague. + +

    Procedure: hash object [ bound ] → integer + +

    Produces a hash value for object in the range ( 0, bound (. If +bound is not given, the implementation is free to choose any bound, +given that the default bound is greater than the size of any imaginable +hash table in a normal application. (This is so that the implementation +may choose some very big value in fixnum range for the default bound.) +This hash function is acceptable for equal?. + +

    Procedure: string-hash string [ bound ] → integer + +

    The same as hash, except that the argument string must be a string. + +

    Procedure: string-ci-hash string [ bound ] → integer + +

    The same as string-hash, except that the case of characters in +string does not affect the hash value produced. + +

    Procedure: hash-by-identity object [ bound ] → integer + +

    The same as hash, except that this function is only guaranteed to be +acceptable for eq?. The reason for providing this function is that +it might be implemented significantly more efficiently than hash. +Implementations are encouraged to provide this function as a builtin. + +

    Implementation

    + +

    This implementation relies on SRFI-9 for distinctness of the hash table +type, and on SRFI-23 for error reporting. Otherwise, the implementation +is pure R5RS. + +

    +
    +(define *default-bound* (- (expt 2 29) 3))
    +
    +(define (%string-hash s ch-conv bound)
    +  (let ((hash 31)
    +	(len (string-length s)))
    +    (do ((index 0 (+ index 1)))
    +      ((>= index len) (modulo hash bound))
    +      (set! hash (modulo (+ (* 37 hash)
    +			    (char->integer (ch-conv (string-ref s index))))
    +			 *default-bound*)))))
    +
    +(define (string-hash s . maybe-bound)
    +  (let ((bound (if (null? maybe-bound) *default-bound* (car maybe-bound))))
    +    (%string-hash s (lambda (x) x) bound)))
    +
    +(define (string-ci-hash s . maybe-bound)
    +  (let ((bound (if (null? maybe-bound) *default-bound* (car maybe-bound))))
    +    (%string-hash s char-downcase bound)))
    +
    +(define (symbol-hash s . maybe-bound)
    +  (let ((bound (if (null? maybe-bound) *default-bound* (car maybe-bound))))
    +    (%string-hash (symbol->string s) (lambda (x) x) bound)))
    +
    +(define (hash obj . maybe-bound)
    +  (let ((bound (if (null? maybe-bound) *default-bound* (car maybe-bound))))
    +    (cond ((integer? obj) (modulo obj bound))
    +	  ((string? obj) (string-hash obj bound))
    +	  ((symbol? obj) (symbol-hash obj bound))
    +	  ((real? obj) (modulo (+ (numerator obj) (denominator obj)) bound))
    +	  ((number? obj)
    +	   (modulo (+ (hash (real-part obj)) (* 3 (hash (imag-part obj))))
    +		   bound))
    +	  ((char? obj) (modulo (char->integer obj) bound))
    +	  ((vector? obj) (vector-hash obj bound))
    +	  ((pair? obj) (modulo (+ (hash (car obj)) (* 3 (hash (cdr obj))))
    +			       bound))
    +	  ((null? obj) 0)
    +	  ((not obj) 0)
    +	  ((procedure? obj) (error "hash: procedures cannot be hashed" obj))
    +	  (else 1))))
    +
    +(define hash-by-identity hash)
    +
    +(define (vector-hash v bound)
    +  (let ((hashvalue 571)
    +	(len (vector-length v)))
    +    (do ((index 0 (+ index 1)))
    +      ((>= index len) (modulo hashvalue bound))
    +      (set! hashvalue (modulo (+ (* 257 hashvalue) (hash (vector-ref v index)))
    +			      *default-bound*)))))
    +
    +(define %make-hash-node cons)
    +(define %hash-node-set-value! set-cdr!)
    +(define %hash-node-key car)
    +(define %hash-node-value cdr)
    +
    +(define-record-type <srfi-hash-table>
    +  (%make-hash-table size hash compare associate entries)
    +  hash-table?
    +  (size hash-table-size hash-table-set-size!)
    +  (hash hash-table-hash-function)
    +  (compare hash-table-equivalence-function)
    +  (associate hash-table-association-function)
    +  (entries hash-table-entries hash-table-set-entries!))
    +
    +(define *default-table-size* 64)
    +
    +(define (appropriate-hash-function-for comparison)
    +  (or (and (eq? comparison eq?) hash-by-identity)
    +      (and (eq? comparison string=?) string-hash)
    +      (and (eq? comparison string-ci=?) string-ci-hash)
    +      hash))
    +
    +(define (make-hash-table . args)
    +  (let* ((comparison (if (null? args) equal? (car args)))
    +	 (hash
    +	   (if (or (null? args) (null? (cdr args)))
    +	     (appropriate-hash-function-for comparison) (cadr args)))
    +	 (size
    +	   (if (or (null? args) (null? (cdr args)) (null? (cddr args)))
    +	     *default-table-size* (caddr args)))
    +	 (association
    +	   (or (and (eq? comparison eq?) assq)
    +	       (and (eq? comparison eqv?) assv)
    +	       (and (eq? comparison equal?) assoc)
    +	       (letrec
    +		 ((associate
    +		    (lambda (val alist)
    +		      (cond ((null? alist) #f)
    +			    ((comparison val (caar alist)) (car alist))
    +			    (else (associate val (cdr alist)))))))
    +		 associate))))
    +    (%make-hash-table 0 hash comparison association (make-vector size '()))))
    +
    +(define (make-hash-table-maker comp hash)
    +  (lambda args (apply make-hash-table (cons comp (cons hash args)))))
    +(define make-symbol-hash-table
    +  (make-hash-table-maker eq? symbol-hash))
    +(define make-string-hash-table
    +  (make-hash-table-maker string=? string-hash))
    +(define make-string-ci-hash-table
    +  (make-hash-table-maker string-ci=? string-ci-hash))
    +(define make-integer-hash-table
    +  (make-hash-table-maker = modulo))
    +
    +(define (%hash-table-hash hash-table key)
    +  ((hash-table-hash-function hash-table)
    +     key (vector-length (hash-table-entries hash-table))))
    +
    +(define (%hash-table-find entries associate hash key)
    +  (associate key (vector-ref entries hash)))
    +
    +(define (%hash-table-add! entries hash key value)
    +  (vector-set! entries hash
    +	       (cons (%make-hash-node key value)
    +		     (vector-ref entries hash))))
    +
    +(define (%hash-table-delete! entries compare hash key)
    +  (let ((entrylist (vector-ref entries hash)))
    +    (cond ((null? entrylist) #f)
    +	  ((compare key (caar entrylist))
    +	   (vector-set! entries hash (cdr entrylist)) #t)
    +	  (else
    +	    (let loop ((current (cdr entrylist)) (previous entrylist))
    +	      (cond ((null? current) #f)
    +		    ((compare key (caar current))
    +		     (set-cdr! previous (cdr current)) #t)
    +		    (else (loop (cdr current) current))))))))
    +
    +(define (%hash-table-walk proc entries)
    +  (do ((index (- (vector-length entries) 1) (- index 1)))
    +    ((< index 0)) (for-each proc (vector-ref entries index))))
    +
    +(define (%hash-table-maybe-resize! hash-table)
    +  (let* ((old-entries (hash-table-entries hash-table))
    +	 (hash-length (vector-length old-entries)))
    +    (if (> (hash-table-size hash-table) hash-length)
    +      (let* ((new-length (* 2 hash-length))
    +	     (new-entries (make-vector new-length '()))
    +	     (hash (hash-table-hash-function hash-table)))
    +	(%hash-table-walk
    +	  (lambda (node)
    +	    (%hash-table-add! new-entries
    +			      (hash (%hash-node-key node) new-length)
    +			      (%hash-node-key node) (%hash-node-value node)))
    +	  old-entries)
    +	(hash-table-set-entries! hash-table new-entries)))))
    +
    +(define (hash-table-ref hash-table key . maybe-default)
    +  (cond ((%hash-table-find (hash-table-entries hash-table)
    +			   (hash-table-association-function hash-table)
    +			   (%hash-table-hash hash-table key) key)
    +	 => %hash-node-value)
    +	((null? maybe-default)
    +	 (error "hash-table-ref: no value associated with" key))
    +	(else ((car maybe-default)))))
    +
    +(define (hash-table-ref/default hash-table key default)
    +  (hash-table-ref hash-table key (lambda () default)))
    +
    +(define (hash-table-set! hash-table key value)
    +  (let ((hash (%hash-table-hash hash-table key))
    +	(entries (hash-table-entries hash-table)))
    +    (cond ((%hash-table-find entries
    +			     (hash-table-association-function hash-table)
    +			     hash key)
    +	   => (lambda (node) (%hash-node-set-value! node value)))
    +	  (else (%hash-table-add! entries hash key value)
    +		(hash-table-set-size! hash-table
    +				       (+ 1 (hash-table-size hash-table)))
    +		(%hash-table-maybe-resize! hash-table)))))
    +
    +(define (hash-table-update! hash-table key function . maybe-default)
    +  (let ((hash (%hash-table-hash hash-table key))
    +	(entries (hash-table-entries hash-table)))
    +    (cond ((%hash-table-find entries
    +			     (hash-table-association-function hash-table)
    +			     hash key)
    +	   => (lambda (node)
    +	        (%hash-node-set-value!
    +		  node (function (%hash-node-value node)))))
    +	  ((null? maybe-default)
    +	   (error "hash-table-update!: no value exists for key" key))
    +	  (else (%hash-table-add! entries hash key
    +				  (function ((car maybe-default))))
    +		(hash-table-set-size! hash-table
    +				       (+ 1 (hash-table-size hash-table)))
    +		(%hash-table-maybe-resize! hash-table)))))
    +
    +(define (hash-table-update!/default hash-table key function default)
    +  (hash-table-update! hash-table key function (lambda () default)))
    +
    +(define (hash-table-delete! hash-table key)
    +  (if (%hash-table-delete! (hash-table-entries hash-table)
    +			   (hash-table-equivalence-function hash-table)
    +			   (%hash-table-hash hash-table key) key)
    +    (hash-table-set-size! hash-table (- (hash-table-size hash-table) 1))))
    +
    +(define (hash-table-exists? hash-table key)
    +  (and (%hash-table-find (hash-table-entries hash-table)
    +			 (hash-table-association-function hash-table)
    +			 (%hash-table-hash hash-table key) key) #t))
    +
    +(define (hash-table-walk hash-table proc)
    +  (%hash-table-walk
    +    (lambda (node) (proc (%hash-node-key node) (%hash-node-value node)))
    +    (hash-table-entries hash-table)))
    +
    +(define (hash-table-fold hash-table f acc)
    +  (hash-table-walk hash-table 
    +		       (lambda (key value) (set! acc (f key value acc))))
    +  acc)
    +
    +(define (alist->hash-table alist . args)
    +  (let* ((comparison (if (null? args) equal? (car args)))
    +	 (hash
    +	   (if (or (null? args) (null? (cdr args)))
    +	     (appropriate-hash-function-for comparison) (cadr args)))
    +	 (size
    +	   (if (or (null? args) (null? (cdr args)) (null? (cddr args)))
    +	     (max *default-table-size* (* 2 (length alist))) (caddr args)))
    +	 (hash-table (make-hash-table comparison hash size)))
    +    (for-each
    +      (lambda (elem)
    +	(hash-table-update!/default
    +	  hash-table (car elem) (lambda (x) x) (cdr elem)))
    +      alist)
    +    hash-table))
    +
    +(define (hash-table->alist hash-table)
    +  (hash-table-fold hash-table
    +		   (lambda (key val acc) (cons (cons key val) acc)) '()))
    +
    +(define (hash-table-copy hash-table)
    +  (let ((new (make-hash-table (hash-table-equivalence-function hash-table)
    +  			      (hash-table-hash-function hash-table)
    +			      (max *default-table-size*
    +				   (* 2 (hash-table-size hash-table))))))
    +    (hash-table-walk hash-table
    +		     (lambda (key value) (hash-table-set! new key value)))
    +    new))
    +
    +(define (hash-table-merge! hash-table1 hash-table2)
    +  (hash-table-walk
    +    hash-table2
    +    (lambda (key value) (hash-table-set! hash-table1 key value)))
    +  hash-table1)
    +
    +(define (hash-table-keys hash-table)
    +  (hash-table-fold hash-table (lambda (key val acc) (cons key acc)) '()))
    +
    +(define (hash-table-values hash-table)
    +  (hash-table-fold hash-table (lambda (key val acc) (cons val acc)) '()))
    +
    +
    +
    + +

    Copyright

    + +

    Copyright © Panu Kalliokoski (2005). All Rights Reserved. + +

    Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +Software), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +

    The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +

    THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +


    +
    Editor: David Van Horn
    +Last modified: Wed Sep 14 09:54:51 EDT 2005 + + + diff --git a/doc/srfi-std/srfi-7.html b/doc/srfi-std/srfi-7.html new file mode 100644 index 0000000000..17cbb0b4be --- /dev/null +++ b/doc/srfi-std/srfi-7.html @@ -0,0 +1,337 @@ + + + + SRFI 7: Feature-based program configuration language + + + +

    Title

    + +SRFI-7: Feature-based program configuration language + +

    Author

    + +Richard Kelsey + +

    Status

    + +This SRFI is currently in ``final'' status. To see an explanation of each status that a SRFI can hold, see here. +You can access the discussion via the archive of the mailing list. +

      +
    • Received: 1999/05/12 +
    • Draft: 1999/05/26-1999/07/26 +
    • Final: 1999/08/19 +
    + +

    Abstract

    + +

    +This SRFI describes a configuration language to be used for specifing +the set of Scheme features or extensions required to run a program. +In addition to a list of required features, a program may also contain +Scheme code to be used only when a particular feature or combination of +features is available. +

    + +

    +The configuration language is entirely distinct from Scheme; it is +neither embedded in Scheme nor includes Scheme as a subset. +

    + +

    Rationale

    + +

    +The use of a separate configuration language makes it easy for +both human and machine readers to determine the requirements of a +program. It also avoids ambiguities arising from having to expand +macros to determine which features, and thus which macros, are used +in the program. +

    + +

    See SRFI 0 for a +rationale for the need for some kind of configuration control. +

    + +

    Specification

    + + + + + + + + + +

    Syntax

    + +

    +

    + <program> --> (program <program clause>+)
    +
    + <program clause>
    +   --> (requires <feature identifier>+)
    +     | (files <filename>*)
    +     | (code <Scheme expression, definition, or syntax definition>*)
    +     | (feature-cond <feature-cond clause>+)
    +     | (feature-cond <feature-cond clause>* (else <program clause>+))
    +
    + <feature-cond clause>
    +   --> (<feature requirement> <program clause>+)
    +
    + <feature requirement>
    +   --> <feature identifier>
    +     | (and <feature requirement>*)
    +     | (or <feature requirement>*)
    +     | (not <feature requirement>)
    +
    + <feature identifier>
    +   --> a symbol which is the name of a SRFI
    +
    +

    + +

    Semantics

    +

    +The configuration language is distinct from Scheme. Given a set of +available features a <program> can be converted into a +sequence of Scheme commands, definitions, and syntax definitions. +This conversion does not require expanding macros or doing any other +processing of Scheme code. +

    + +

    +An implementation of the configuration language will need to provide +some method for executing a program. For example, it might have a +(LOAD-PROGRAM <filename>) function or a compiler that +compiles a program into an executable file. +

    + +

    The meanings of the different <program> clauses are +given below. The ordering of the clauses in a <program> +determines the order of the forms in the resultant Scheme program. + +

    In processing the REQUIRES and +FEATURE-COND clauses in a <program>, +an implementation should be consistent with some fixed set of present +and absent features. An implementation may analyze a <program> +before choosing a set of features to use in processing it, but it +should not use different sets of features for different clauses in the +same <program>. +

    + +
    +
    (requires <feature-identifier>+)
    +
    The listed features are required by the program. If one or + more is not provided by the implementation the program cannot + be run. +
    + +
    (files <filename>*)
    +
    The listed files are read and the forms they contain added to the program.
    + +
    (code <body>)
    +
    The forms in <body> are added to the program.
    + +
    (feature-cond <feature-cond clause>+)
    +
    The meaning of a FEATURE-COND clause is that of the +<program-clause>s in the first <feature-cond clause> whose +<implementation-requirement> is satisfied by the implementation. +If an ELSE clause is present it is used if and only if no preceding +clause is satisfied; a FEATURE-COND with an +ELSE clause is always satisfied. + +

    If no clause can be satisified the <program> cannot be evaluated in +the implementation.

    + +

    +The meaning of the <implementation requirement>s is as follows:

    + + + + + + + + + + + + + + + + +
    <feature identifier>satisfied if the feature is present
    (and)always satisfied
    (and x ...)satisfied if every + X is satisfied
    (or)never satisfied
    (or x ...)satisfied if any + X is satisfied
    (not x)satisfied if X is not satisfied
    + +
    + +

    Implementation

    + +

    Two implementations are provided here. The first is a +PROCESS-PROGRAM function that converts a +<program>, represented as an S-expression, and a +list of feature identifiers and returns the list expressions, +definitions, and syntax definitions that are the source for the +<program> in the presence of those features. The +function returns #F if the program cannot be run using +the features provided. +

    + +

    +This is not a complete implementation of the configuration language; it needs +an (implementation-dependent) method for evaluating the forms returned by +PROCESS-PROGRAM. +

    + +

    +

    +(define (process-program program features)
    +  (call-with-current-continuation
    +    (lambda (exit)	; We exit early when an unsatisfiable clause is found.
    +
    +      ; Process each clause in turn
    +
    +      (define (process-clauses clauses)
    +	(if (null? clauses)
    +	    '()
    +	    (append (process-clause (car clauses))
    +		    (process-clauses (cdr clauses)))))
    +      
    +      ; Dispatch on the type of the clause.
    +
    +      (define (process-clause clause)
    +	(case (car clause)
    +	  ((requires)
    +	   (if (all-satisfied? (cdr clause))
    +	       '()
    +	       (exit #f)))
    +	  ((code)
    +	   (cdr clause))
    +	  ((files)
    +	   (read-files (cdr clause)))
    +	  ((feature-cond)
    +	   (process-cond-clauses (cdr clause)))))
    +      
    +      ; Loop through CLAUSES finding the first that is satisfied.
    +
    +      (define (process-cond-clauses clauses)
    +	(cond ((null? clauses)
    +	       (exit #f))
    +	      ((or (and (eq? (caar clauses) 'else)
    +			(null? (cdr clauses)))
    +		   (satisfied? (caar clauses)))
    +	       (process-clauses (cdar clauses)))
    +	      (else
    +	       (process-cond-clauses (cdr clauses)))))
    +    
    +      ; Compound requirements are handled recursively, simple ones are tested.
    +
    +      (define (satisfied? requirement)
    +	(if (pair? requirement)
    +	    (case (car requirement)
    +	      ((and)
    +	       (all-satisfied? (cdr requirement)))
    +	      ((or)
    +	       (any-satisfied? (cdr requirement)))
    +	      ((not)
    +	       (not (satisfied? (cadr requirement)))))
    +	    (memq requirement features)))
    +      
    +      ; True if every requirement in LIST is satisfied.
    +
    +      (define (all-satisfied? list)
    +	(if (null? list)
    +	    #t
    +	    (and (satisfied? (car list))
    +		 (all-satisfied? (cdr list)))))
    +      
    +      ; True if any requirement in LIST is satisfied.
    +
    +      (define (any-satisfied? list)
    +	(if (null? list)
    +	    #f
    +	    (or (satisfied? (car list))
    +		(any-satisfied? (cdr list)))))
    +      
    +      ; Start by doing the whole program.
    +
    +      (process-clauses (cdr program)))))
    +
    +; Returns a list of the forms in the named files.
    +
    +(define (read-files filenames)
    +  (if (null? filenames)
    +      '()
    +      (append (call-with-input-file (car filenames)
    +		(lambda (in)
    +		  (let label ()
    +		    (let ((next (read in)))
    +		      (if (eof-object? next)
    +			  '()
    +			  (cons next (label)))))))
    +	      (read-files (cdr filenames)))))
    +

    + +

    +The second implementation is a PROGRAM macro that implements +the configuration language in terms of the COND-EXPAND +syntax of SRFI 0. +Note that this implementation requires that LOAD use the current +evaluation environment. +

    + +

    +(define-syntax program
    +  (syntax-rules (requires files code feature-cond)
    +    ((program)
    +     (begin))
    +    ((program (requires feature-id ...)
    +              more ...)
    +     (begin (cond-expand ((and feature-id ...) 'okay))
    +            (program more ...)))
    +    ((program (files filename ...)
    +              more ...)
    +     (begin (load filename) ...
    +            (program more ...)))
    +    ((program (code stuff ...)
    +              more ...)
    +     (begin stuff ...
    +            (program more ...)))
    +    ((program (feature-cond (requirement stuff ...) ...)
    +              more ...)
    +     (begin (cond-expand (requirement (program stuff ...)) ...)
    +            (program more ...)))))
    +

    + +

    Copyright

    + +Copyright (C) Richard Kelsey (1999). All Rights Reserved. +

    +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: +

    +

    +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. +

    +

    +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +

    + +
    +
    Editor: Mike Sperber
    + + + diff --git a/doc/srfi-std/srfi-71.html b/doc/srfi-std/srfi-71.html new file mode 100644 index 0000000000..7834ac8c12 --- /dev/null +++ b/doc/srfi-std/srfi-71.html @@ -0,0 +1,549 @@ + + + + SRFI 71: Extended LET-syntax for multiple values + + + + +

    Title

    + +Extended LET-syntax for multiple values + +

    Author

    + +Sebastian Egner + +

    +This SRFI is currently in ``final'' status. To see an explanation of each +status that a SRFI can hold, see +here. +To +provide input on this SRFI, please +mailto:srfi-71@srfi.schemers.org. +See instructions +here to subscribe to the list. You can access the discussion via +the +archive of the mailing list. +You can access +post-finalization messages via + +the archive of the mailing list. +

    + + + +

    Abstract

    + +This SRFI is a proposal for extending let, +let*, and letrec +for receiving multiple values. +The syntactic extension is fully compatible with the existing syntax. +It is the intention that single-value bindings, +i.e. (let ((var expr)) ...), and +multiple-value binding can be mixed freely and conveniently. +

    +The most simple form of the new syntax is best explained by an example: +

    +

    (define (quo-rem x y)
    +  (values (quotient x y) (remainder x y)))
    +
    +(define (quo x y)
    +  (let ((q r (quo-rem x y)))
    +    q))
    +
    +The procedure quo-rem delivers two values to +its continuation. These values are received as q +and r in the let-expression of the +procedure quo. +In other words, the syntax of let is extended such +that several variables can be specified---and these variables +receive the values delivered by the expression (quo-rem x y). +

    +The syntax of let is further extended to cases in which +a rest argument receives the list of all residual values. +Again by example, +

    (let (((values y1 y2 . y3+) (foo x)))
    +   body)
    +
    +In this example, values is a syntactic keyword +indicating the presence of multiple values to be received, +and y1, y2, and y3+, +resp., are variables bound to the first value, the second value, +and the list of the remaining values, resp., as produced by +(foo x). +The syntactic keyword values allows receiving +all values as in (let (((values . xs) (foo x))) body). +It also allows receiving no values at all as in +(let (((values) (for-each foo list))) body). +

    +A common application of binding multiple values is +decomposing data structures into their components. +This mechanism is illustrated in its most primitive form as follows: +The procedure uncons (defined below) +decomposes a pair x into its car and its cdr +and delivers them as two values to its continuation. +Then an extended let can receive these values: +

    (let ((car-x cdr-x (uncons x)))
    +  (foo car-x cdr-x))
    +
    +Of course, for pairs this method is probably neither faster +nor clearer than using the procedures car +and cdr. +However, for data structures doing substantial work upon +decomposition this is different: Extracting the element of +highest priority from a priority queue, while at the +same time constructing the residual queue, +can both be more efficient and more convenient than +doing both operations independently. +In fact, the quo-rem example illustrates this +point already as both quotient and remainder are probably +computed by a common exact division algorithm. +(And often caching is used to avoid executing this +algorithm twice as often as needed.) +

    +As the last feature of this SRFI, a mechanism is specified +to store multiple values in heap-allocated data structures. +For this purpose, values->list and values->vector +construct a list (a vector, resp.) storing all values delivered +by evaluating their argument expression. +Note that these operations cannot be procedures. + +

    Rationale

    + +My original motivation for writing this SRFI is my unhappiness with +the current state of affairs in Scheme with respect to multiple values. +Multiple values are mandatory in the +Revised^5 Report on the Algorithmic +Language Scheme (R5RS), +and they are fully available in all major Scheme implementations. +Yet there is often a painful hesitation about using them. +

    +The reason for this hesitation is that multiple values are +nearly fully integrated into Scheme---but not quite. +(Unlike for example in the languages Matlab, Octave, or +the computer algebra system Magma, in which returning +multiple values from a function is the most natural thing +in the world: q, r := quo_rem(x, y);) +However, considerable progress has been made on this point, +and I understand this SRFI as a minor contribution +"placing the last corner stone". +But first a very brief history of multiple values in Scheme, +as far as relevant for this SRFI. +

    +R5RS specifies the procedures values +and call-with-values for passing any number of values +from a producer procedure to a consumer procedure. +This is the only construct in R5RS +dealing with multiple values explicitly, and it is sufficient +to implement anything that can be implemented for multiple values. +

    +However, as John David Stone observed in SRFI 8, +the mechanism exposes explicitly how multiple values are +passed---but that is hardly ever interesting. +In fact, call-with-values is often clumsy because +the continuations are made explicit. +SRFI 8 improves on this situation +by adding the special form +(receive <formals> <expression> <body>) +for receiving several values produced by the expression +in variables specified by <formals> and +using them in the body. +

    +The major limitation of receive is that it can only +handle a single expression, which means programs dealing with +multiple values frequently get deeply nested. +SRFI 11 provides a more versatile construct: +Let-values and let*-values are +modeled after let and let* but +replace <variable> by an argument list +with the syntax of <formals>. +The let-values binding construct makes multiple +values about as convenient as it will ever get in Scheme. +Its primary shortcoming is that let-values is +incompatible with the existing syntax of let: +In (let-values ((v x)) ...) is v bound +to the list of all values delivered by x (as in SRFI 11)? +Or is x to deliver a single value to be +bound to v (as in let)? +Refer to the +discussion +archive of SRFI 11 for details. +Moreover, let-values suffers from "parenthesis complexity", +despite Scheme programmers are tolerant to braces. +

    +Eli Barzilay's Swindle library (for MzScheme) on +the other hand redefines let to include multiple-values and internal +procedures: The syntactic keyword values indicates the +presence of multiple values, while additional parentheses (with the +syntax of <formals>) +indicate a lambda-expression as right-hand side. +

    +This SRFI follows Eli's approach, while keeping the syntax +simple (few parentheses and concepts) and adding tools for +dealing more conveniently with multiple values. +The aim is convenient integration of multiple values into Scheme, +at full coexistence with the existing syntax (R5RS.) +This is achieved by extending the syntax in two different +ways (multiple left-hand sides or a syntactic keyword) +and adding operations to convert between (implicitly passed) +values and (first class) data structures. +

    +Finally, I would like to mention that Oscar Waddell et al. +describe an efficient compilation method for Scheme's +letrec (Fixing Letrec) +and propose a letrec* binding construct +to as a basis for internal define. +I expect their compilation method (and letrec*) +and this SRFI to be fully compatible with one another, +although I have not checked this claim by way of implementation. + +

    Specification

    + +The syntax of Scheme (R5RS, Section 7.1.3.) +is extended by replacing the existing production: + +
    <binding spec> --> (<variable> <expression>)
    +
    + +by the three new productions + +
    <binding spec> --> ((values <variable>*) <expression>)
    +<binding spec> --> ((values <variable>* . <variable>) <expression>)
    +<binding spec> --> (<variable>+ <expression>)
    +
    + +The form (<variable>+ <expression>) is just +an abbreviation for ((values <variable>+) <expression>), +and it includes the original <binding spec> of R5RS. +

    +The first two forms are evaluated as follows: The variables are bound and +the expression is evaluated according to the enclosing construct +(either let, let*, or letrec.) +However, the expression may deliver any number of values to its continuation, +which stores these values into the variables specified, +possibly allocating a rest list in case of the . <variable> form. +

    +The number of values delivered by the expression must match the +number of values expected by the binding specification. +Otherwise an error is raised, as call-with-values would. +This implies in particular, that each binding of a named let involves +exactly one value, because this binding can also be an argument to a +lambda-expression. + +

    Standard operations

    + +The following procedures, specified in terms of standard procedures, +are added to the set of standard procedures: +
    (define (uncons pair)
    +  (values (car pair) (cdr pair)))
    +
    +(define (uncons-2 list)
    +  (values (car list) (cadr list) (cddr list)))
    +
    +(define (uncons-3 list)
    +  (values (car list) (cadr list) (caddr list) (cdddr list)))
    +
    +(define (uncons-4 list)
    +  (values (car list) (cadr list) (caddr list) (cadddr list) (cddddr list)))
    +
    +(define (uncons-cons alist)
    +  (values (caar alist) (cdar alist) (cdr alist)))
    +
    +(define (unlist list)
    +  (apply values list))
    +
    +(define (unvector vector)
    +  (apply values (vector->list vector)))
    +
    +These procedures decompose the standard concrete data structures +(pair, list, vector) and deliver the components as values. +It is an error if the argument cannot be decomposed as expected. +Note that the procedures are not necessarily implemented by +the definition given above. +

    +The preferred way of decomposing a list into the first two elements +and the rest list is (let ((x1 x2 x3+ (uncons-2 x))) body), +and similar for three or four elements and a rest. +This is not equivalent to +(let (((values x1 x2 . x3+) (unlist x))) body) +because the latter binds x3+ to a newly allocated +copy of (cddr x). +

    +Finally, the following two macros are added to the standard macros: + +

    (values->list   <expression>)
    +(values->vector <expression>)
    +
    + +These operation receive all values (if any) delivered by their +argument expression and return a newly allocated list (vector, resp.) +of these values. +Note that values->list is not the same as +list (the procedure returning the list of its arguments). + +

    Design Rationale

    + +

    Which alternatives designs for the syntax were considered?

    + +This SRFI defines two notations for receiving several values: +Using the keyword values, +or simply listing the variables if there is at least one. +There are several alternatives for this design, +some of which were proposed during the discussion. +(Refer in particular to msg00000, +msg00001, and +msg00002, +msg00007.) +The alternatives considered include: +
      +
    1. Just listing the variables (no syntactic keyword at all) as in +(let ((x1 x2 expr)) body).
    2. +
    3. Only using a keyword, e.g. values, as in +(let (((values x1 x2 . x3+) expr)) body).
    4. +
    5. A keyword, e.g. dot, indicating the +rest list as in (let ((x1 x2 dot x3+ expr)) body).
    6. +
    7. The keyword ... indicating the +rest list as in (let ((x1 x2 x3+ ... expr)) body).
    8. +
    9. A keyword, e.g. rest, indicating the +rest list as in (let ((x1 x2 (rest x3+) expr)) body).
    10. +
    11. Using the <formals> syntax of +R5RS as in +(let (((x1 x2 . x3+) expr)) body). +
    12. Mimicking <formals> but with +one level of parentheses removed as in +(let ((x1 x2 . x3+ expr)) body).
    13. +
    14. As the previous but with additional syntax for +"no values" and "just a rest", e.g. +(let ((! expr)) body) and +(let ((xs . expr)) body). +
    +

    +The requirements for the design are +compatibility with the existing let, +concise notation for the frequent use cases, +robustness against most common mistakes, and +full flexibility of receiving values. +

    +For the sake of compatibility, +only modifications of <binding spec> were +considered. +The alternative, i.e. modifying the syntax of let +in other ways, was not considered. +Concerning concise notation, by far the most convenient notation +is listing the variables. +As this notation also covers the existing syntax, it was adopted +as the basis of the extension to be specified. +

    +The listing the variables notation is limited by the fact that +the preferred marker for a rest list (".") +cannot follow an opening parenthesis as in +(let ((. xs expr)) body), +nor that it can be followed by two syntactic elements as in +(let ((x1 . x2+ expr)) body). +Lifting these restrictions would require major modifications +in unrelated parts of the Scheme syntax, which is not an +attractive option. +

    +Another problematic aspect of the listing the variables notation +is the case of no variables at all. +While this case is not conflicting with existing syntax, +it seriously harms syntactic robustness: +(let (((run! foo))) body) and +(let ((run! foo)) body) would both be +syntactically correct and could easily be confused with one another. +For this reason, the notation of listing the variables was +restricted to one or more variables. +

    +This leaves the problem of extending the notation in order to +cover rest arguments and the "no values"-case. +This can either be done ad hoc, covering the open cases, +or by adding a general notation covering all cases. +In view of readability and uniformity (useful when code gets +processed automatically) the latter approach was chosen. +This has resulted in the design specified in this SRFI. + +

    Why is values needed in the "zero values"-case?

    + +The syntax specified in this SRFI allows zero variables being +bound in a binding specification using the syntax +(let (((values) (for-each foo (bar)))) body). +An alternative is allowing (<expression>) +as a binding specification. +(Refer to the discussion archive starting at msg00001.) +

    +The syntax specified in this SRFI is designed for static +detection of the most frequent types (forgotten parentheses). +For example, writing +(let ((values) (for-each foo (bar))) body) +is not a well-formed let-expression in this SRFI. +

    +In the alternative syntax, both +(let (((for-each foo (bar)))) body) +and (let ((for-each foo (bar))) body) +are syntactically correct. +The first just executes (for-each foo (bar)), +whereas the second binds the variables for-each and +foo to the two values delivered by (bar). +Assuming the first meaning was intended, +the error will probably manifest itself at the moment +(bar) fails to deliver exactly two values. +Unless it does, in which case the error must manifest itself much +further downstream from the fact that foo never got called. +

    +In order to avoid this sort of expensive typos, +the syntax proposed in this SRFI is more verbose +than it needs to be. + +

    Why not also include a syntax for procedures?

    + +This SRFI is a proposal for extending the syntax of let +etc. in order to include multiple values. +It is also desirable to extend the syntax of let +for simplifying the definition of local procedures. +(For example, as in Swindle.) +However, this SRFI does not include this feature. +

    +The reason I have chosen not restrict this SRFI to a syntax +for multiple values is simplicity. + +

    Why the names unlist etc.?

    + +An alternative naming convention for the decomposition +operation unlist is list->values, +which is more symmetric with respect to its +inverse operation values->list. +

    +This symmetry ends, however, as soon as more complicated +data structures with other operations are involved. +Then it becomes apparent that the same data structure can +support different decomposition operations: +A double-ended queue (deque) for example supports splitting off +the head and splitting of the tail; and neither of these +operations should be named deque->values. +The un-convention covers this in a natural way. +

    +Please also refer to the double-ended queue (deque) example +in examples.scm to see how to +use decomposition procedures for dealing with data structures. + +

    Which decomposition operations are included?

    + +The particular set of operations specified in this SRFI +for decomposing lists represents a trade-off between limiting +the number of operations and convenience. +

    +As Al Petrofsky has pointed out during the discussion +( +msg00018) it is not sufficient to have only +unlist as this will copy the rest list. +For this reason specialized decomposition operations +for splitting off the first 1, ..., 4 elements are +provided, and a decomposition operation expecting the +first element to be a pair itself. +These appear to be the most common cases. + +

    Implementation

    + +The reference implementation is written in R5RS +using hygienic macros, only. +It is not possible, however, to portably detect read access to +an uninitialized variable introduced by letrec. +The definition of the actual functionality can be found +here. +The implementation defines macros srfi-let/*/rec etc. +in terms of r5rs-let/*/rec. +Implementors may use this to redefine (or even re-implement) +let/*/rec in terms of srfi-let/*/rec, +while providing implementations of r5rs-let/*/rec. +An efficient method for the latter is given in Fixing Letrec +by O. Waddell et al. +

    +R5RS: +For trying out the functionality, a complete implementation under +R5RS can be found here. +It defines r5rs-let/*/rec in terms of lambda +and redefines let/*/rec as srfi-let/*/rec. +This may not be the most efficient implementation, because many +Scheme systems handle let etc. specially and do not +reduce it into lambda +

    +PLT 208: +The implementation found here +uses PLT's module system for exporting +srfi-let/*/rec +under the name of let/*/rec, while defining +r5rs-let/*/rec as a copy of the built-in +let/*/rec. This code should be efficient. +

    +Examples using the new functionality +can be found in examples.scm. + +

    References

    + + + + + + + + + + + + + + + + + + +
    [R5RS] +Richard Kelsey, William Clinger, and Jonathan Rees (eds.): +Revised^5 Report on the Algorithmic Language Scheme of +20 February 1998. +Higher-Order and Symbolic Computation, Vol. 11, No. 1, September 1998. + +http://schemers.org/Documents/Standards/R5RS/. +
    [SRFI 8] +John David Stone: Receive: Binding to multiple values. +http://srfi.schemers.org/srfi-8/ +
    [SRFI 11] +Lars T. Hansen: Syntax for receiving multiple values. +http://srfi.schemers.org/srfi-11/ +
    [Swindle] +Eli Barzilay: Swindle, documentation for "base.ss" (Swindle Version 20040908.) +http://www.cs.cornell.edu/eli/Swindle/base-doc.html#let +
    [Fix] +O. Waddell, D. Sarkar, R. K. Dybvig: +Fixing Letrec: A Faithful Yet Efficient Implementation of Scheme's +Recursive Binding Construct. To appear, 2005. +http://www.cs.indiana.edu/~dyb/pubs/fixing-letrec.pdf +
    + + +

    Copyright

    +Copyright (c) 2005 Sebastian Egner. +

    +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ``Software''), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +

    +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +

    +THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +


    +
    Author: Sebastian Egner
    +
    Editor: Mike Sperber
    + + +Last modified: Sun Sep 11 16:07:38 CEST 2005 + + + \ No newline at end of file diff --git a/doc/srfi-std/srfi-74.html b/doc/srfi-std/srfi-74.html new file mode 100644 index 0000000000..50be38507c --- /dev/null +++ b/doc/srfi-std/srfi-74.html @@ -0,0 +1,60 @@ + + + + +SRFI 74: Octet-Addressed Binary Blocks + + + +

    Title

    + +Octet-Addressed Binary Blocks + +

    Authors

    + +Michael Sperber + +

    +This SRFI is currently in ``final'' status. To see an explanation of each +status that a SRFI can hold, see +here. +To +provide input on this SRFI, please +mailto:srfi-74@srfi.schemers.org. +See instructions +here to subscribe to the list. You can access previous messages via +the +archive of the mailing list. + +

    + +

    Abstract

    This SRFI defines a set of procedures for creating, accessing, and manipulating octet-addressed blocks of binary data, in short, blobs. The SRFI provides access primitives for fixed-length integers of arbitrary size, with specified endianness, and a choice of unsigned and two's complement representations.

    Rationale

    Many applications must deal with blocks of binary data by accessing them in various ways---extracting signed or unsigned numbers of various sizes. Such an application can use octet vectors as in SRFI 66 or any of the other types of homogeneous vectors in SRFI 4, but these both only allow retrieving the binary data of one type.

    This is awkward in many situations, because an application might access different kinds of entities from a single binary block. Even for uniform blocks, the disjointness of the various vector data types in SRFI 4 means that, say, an I/O API needs to provide an army of procedures for each of them in order to provide efficient access to binary data.

    Therefore, this SRFI provides a single type for blocks of binary data with multiple ways to access that data. It deals only with integers in various sizes with specified endianness, because these are the most frequent applications. Dealing with other kinds of binary data, such as floating-point numbers or variable-size integers would be natural extensions, but are left for a future SRFI.

    Specification

    General remarks

    Blobs are objects of a new type. Conceptually, a blob represents a sequence of octets.

    Scheme systems implementing both SRFI 4 and/or SRFI 66 and this SRFI may or may not use the same type for u8vector and blobs. They are encouraged to do so, however.

    As with u8vectors, the length of a blob is the number of octets it contains. This number is fixed. A valid index into a blob is an exact, non-negative integer. The first octet of a blob has index 0, the last octet has an index one less than the length of the blob.

    Generally, the access procedures come in different flavors according to the size of the represented integer, and the endianness of the representation. The procedures also distinguish signed and unsigned representations. The signed representations all use two's complement.

    For procedures that have no "natural" return value, this SRFI often uses the sentence

    The return values are unspecified.

    This means that number of return values and the return values are unspecified. However, the number of return values is such that it is accepted by a continuation created by begin. Specifically, on Scheme implementations where continuations created by begin accept an arbitrary number of arguments (this includes most implementations), it is suggested that the procedure return zero return values.

    Interface

    (endianness big) (syntax)
    (endianness little) (syntax)
    (endianness native) (syntax)

    (endianness big) and (endianness little) evaluate to two distinct and unique objects representing an endianness. The native endianness evaluates to the endianness of the underlying machine architecture, and must be eq? to either (endianness big) or (endianness little).

    (blob? obj)

    Returns #t if obj is a blob, otherwise returns #f.

    (make-blob k)

    Returns a newly allocated blob of k octets, all of them 0.

    (blob-length blob)

    Returns the number of octets in blob as an exact integer.

    (blob-u8-ref blob k)
    (blob-s8-ref blob k)

    K must be a valid index of blob.

    Blob-u8-ref returns the octet at index k of blob.

    Blob-s8-ref returns the exact integer corresponding to the two's complement representation at index k of blob.

    (blob-u8-set! blob k octet)
    (blob-s8-set! blob k byte)

    K must be a valid index of blob.

    Blob-u8-set! stores octet in element k of blob.

    Byte, must be an exact integer in the interval {-128, ..., 127}. Blob-u8-set! stores the two's complement representation of byte in element k of blob.

    The return values are unspecified.

    (blob-uint-ref size endianness blob k)
    (blob-sint-ref size endianness blob k)
    (blob-uint-set! size endianness blob k n)
    (blob-sint-set! size endianness blob k n)

    Size must be a positive exact integer. K must be a valid index of blob; so must the indices {k, ..., k + size - 1}. Endianness must be an endianness object.

    Blob-uint-ref retrieves the exact integer corresponding to the unsigned representation of size size and specified by endianness at indices {k, ..., k + size - 1}.

    Blob-sint-ref retrieves the exact integer corresponding to the two's complement representation of size size and specified by endianness at indices {k, ..., k + size - 1}.

    For blob-uint-set!, n must be an exact integer in the interval [0, (256^size)-1]. Blob-uint-set! stores the unsigned representation of size size and specified by endianness into the blob at indices {k, ..., k + size - 1}.

    For blob-uint-set!, n must be an exact integer in the interval [-256^(size-1), (256^(size-1))-1]. Blob-sint-set! stores the two's complement representation of size size and specified by endianness into the blob at indices {k, ..., k + size - 1}.

    (blob-u16-ref endianness blob k)
    (blob-s16-ref endianness blob k)
    (blob-u16-native-ref blob k)
    (blob-s16-native-ref blob k)
    (blob-u16-set! endianness blob k n)
    (blob-s16-set! endianness blob k n)
    (blob-u16-native-set! blob k n)
    (blob-s16-native-set! blob k n)

    K must be a valid index of blob; so must the index k+ 1. Endianness must be an endianness object.

    These retrieve and set two-octet representations of numbers at indices k and k+1, according to the endianness specified by endianness. The procedures with u16 in their names deal with the unsigned representation, those with s16 with the two's complement representation.

    The procedures with native in their names employ the native endianness, and only work at aligned indices: k must be a multiple of 2. It is an error to use them at non-aligned indices.

    (blob-u32-ref endianness blob k)
    (blob-s32-ref endianness blob k)
    (blob-u32-native-ref blob k)
    (blob-s32-native-ref blob k)
    (blob-u32-set! endianness blob k n)
    (blob-s32-set! endianness blob k n)
    (blob-u32-native-set! blob k n)
    (blob-s32-native-set! blob k n)

    K must be a valid index of blob; so must the indices {k, ..., k+ 3}. Endianness must be an endianness object.

    These retrieve and set four-octet representations of numbers at indices {k, ..., k+ 3}, according to the endianness specified by endianness. The procedures with u32 in their names deal with the unsigned representation, those with s32 with the two's complement representation.

    The procedures with native in their names employ the native endianness, and only work at aligned indices: k must be a multiple of 4. It is an error to use them at non-aligned indices.

    (blob-u64-ref endianness blob k)
    (blob-s64-ref endianness blob k)
    (blob-u64-native-ref blob k)
    (blob-s64-native-ref blob k)
    (blob-u64-set! endianness blob k n)
    (blob-s64-set! endianness blob k n)
    (blob-u64-native-set! blob k n)
    (blob-s64-native-set! blob k n)

    K must be a valid index of blob; so must the indices {k, ..., k+ 7}. Endianness must be an endianness object.

    These retrieve and set eight-octet representations of numbers at indices {k, ..., k+ 7}, according to the endianness specified by endianness. The procedures with u64 in their names deal with the unsigned representation, those with s64 with the two's complement representation.

    The procedures with native in their names employ the native endianness, and only work at aligned indices: k must be a multiple of 8. It is an error to use them at non-aligned indices.

    (blob=? blob-1 blob-2)

    Returns #t if blob-1 and blob-2 are equal---that is, if they have the same length and equal octets at all valid indices.

    (blob-copy! source source-start target target-start n)

    Copies data from blob source to blob target. Source-start, target-start, and n must be non-negative exact integers that satisfy

    0 <= source-start <= source-start + n <= (blob-length source)

    0 <= target-start <= target-start + n <= (blob-length target)

    This copies the octets from source at indices [source-start, source-start + n) to consecutive indices in target starting at target-index.

    This must work even if the memory regions for the source and the target overlap, i.e., the octets at the target location after the copy must be equal to the octets at the source location before the copy.

    The return values are unspecified.

    (blob-copy blob)

    Returns a newly allocated copy of blob blob.

    (blob->u8-list blob)
    (u8-list->blob blob)

    blob->u8-listreturns a newly allocated list of the octets of blob in the same order.

    U8-list->blob returns a newly allocated blob whose elements are the elements of list octets, which must all be octets, in the same order. Analogous to list->vector.

    (blob->uint-list size endianness blob)
    (blob->sint-list size endianness blob)
    (uint-list->blob size endianness list)
    (sint-list->blob size endianness list)

    Size must be a positive exact integer. Endianness must be an endianness object.

    These convert between lists of integers and their consecutive representations according to size and endianness in blobs in the same way as blob->u8-list, blob->s8-list, u8-list->blob, and s8-list->blob do for one-octet representations.

    Reference Implementation

    This reference implementation makes use of SRFI 23 (Error reporting mechanism), SRFI 26 (Notation for Specializing Parameters without Currying), SRFI 60 (Integers as Bits), and SRFI 66 (Octet Vectors) .

    Examples

    The test suite doubles as a source of examples.

    References

    Copyright

    +Copyright (C) Michael Sperber (2005). All Rights Reserved. +

    +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: +

    +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. +

    +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +


    +
    Editor: David Van Horn
    + diff --git a/doc/srfi-std/srfi-78.html b/doc/srfi-std/srfi-78.html new file mode 100644 index 0000000000..8edbb93f08 --- /dev/null +++ b/doc/srfi-std/srfi-78.html @@ -0,0 +1,312 @@ + + + + SRFI 78: Lightweight testing + + + + +

    Title

    + +Lightweight testing + +

    Author

    + +Sebastian Egner + +

    Status

    + +This SRFI is currently in ``final'' status. To see an explanation of +each status that a SRFI can hold, see here. + +To provide input on this SRFI, please mail to +<srfi-78@srfi.schemers.org>. See instructions here to subscribe to +the list. You can access previous messages via the archive of the mailing list. + + +

      +
    • Received: 2005/10/25
    • +
    • Draft: 2005/11/11 - 2006/01/10
    • +
    • Draft extended: 2006/01/17 - 2006/01/31
    • +
    • Revised: 2006/01/18
    • +
    • Revised: 2006/02/08
    • +
    • Final: 2006/03/06
    • +
    + +

    Abstract

    + +A simple mechanism is defined for testing Scheme programs. +As a most primitive example, the expression + +
       (check (+ 1 1) => 3)
    +
    + +evaluates the expression (+ 1 1) and compares the result +with the expected result 3 provided after the syntactic +keyword =>. Then the outcome of this comparison is reported +in human-readable form by printing a message of the form + +
       (+ 1 1) => 2 ; *** failed ***
    +   ; expected result: 3
    +
    + +Moreover, the outcome of any executed check is recorded +in a global state counting the number of correct and failed +checks and storing the first failed check. At the end of a +file, or at any other point, the user can print a summary +using check-report. +

    +In addition to the simple test above, it is also possible +to execute a parametric sequence of checks. Syntactically, +this takes the form of an eager comprehension in the sense +of SRFI 42 [5]. For example, + +

       (check-ec (:range e 100)
    +             (:let x (expt 2.0 e)) 
    +             (= (+ x 1) x) => #f (e x))
    +
    + +This statement runs the variable e through {0..99} and +for each binding defines x as (expt 2.0 e). Then it is +checked if (+ x 1) is equal to x, and it is expected that +this is not the case (i.e. expected value is #f). The +trailing (e x) tells the reporting mechanism to print +the values of both e and x in case of a failed check. +The output could look like this: + +
       (let ((e 53) (x 9007199254740992.0)) (= (+ x 1) x)) => #t ; *** failed ***
    +    ; expected result: #f
    +
    + +The specification of bindings to report, (e x) in the +example, is optional but very informative. + +Other features of this SRFI are: +
      +
    • A way to specify a different equality predicate (default is equal?).
    • +
    • Controlling the amount of reporting being printed.
    • +
    • Switching off the execution and reporting of checks entriely.
    • +
    • Retrieving a boolean if all checks have been executed and passed.
    • +
    + +

    Rationale

    +The mechanism defined in this SRFI should be available in +every Scheme system because it has already proven useful +for interactive development---of SRFIs. +

    +Although it is extremely straight-forward, the origin of the +particular mechanism described here is the 'examples.scm' file +accompanying the reference implementation of SRFI 42 [5]. +The same mechanism has been reimplemented for the reference +implementation of SRFI 67, and a simplified version is yet +again found in the reference implementation of SRFI 77. +

    +The mechanism in this SRFI does not replace more sophisticated +approaches to unit testing, like SRFI 64 [1] or SchemeUnit [2]. +These systems provide more control of the testing, separate +the definition of a test, its execution, and its reports, and +provide several other features. +

    +Neil Van Dyke's Testeez library [3] is very close in spirit +to this SRFI. In Testeez, tests are disabled by (re-)defining a +macro. The advantage of this method is that the code for the +test cases can be removed entirely, and hence even the dependency +on the Testeez library. This SRFI on the other hand, uses a +Scheme conditional (COND, IF) to prevent execution of the +testing code. This method is more dynamic but retains dead +testing code, unless a compiler and a module system are used +to apply constant folding and dead code elimination. The only +major addition in SRFI over Testeez is the comprehension for +formulating parametric tests. +

    +Design considerations for this SRFI include the following: +

      +
    • Reporting is human-readable and as specific as possible, + i.e. not just "assertion failed" but the expression with + actual and expected value, and if possibly the relevant + part of the bindings environment.
    • +
    • An effort is made to print closed Scheme expressions, i.e. + expressions that can directly be copy/pasted into a REPL + for further analysis (e.g. the let expression in the abstract).
    • +
    • By default the checks report both correct and failed checks. + However, it is possible to reduce the output---or even to + switch off the execution of checks. It has turned out useful + to be able to run only some subset checks for the features + currently under development. This can be done by changing + the reporting mode between differnt sections.
    • +
    • The global state (correct/failed count) is not made available + to the user program. This reduces the dependencies between + different checks because it is not possible to use the state.
    • +
    • Ocassionally, it is useful to check that a certain expression + does not yield an ordinary result but raises an error. However, + R5RS [4] does not specify the mechanism by which this occurs + (e.g. raising exception, breaking into a REPL, aborting the + program, etc.). For this reason, this SRFI is restricted to + the case that the checked expressions evaluate normally.
    • +
    • Though usually I am very much in favor of strictly prefix + syntax, for this SRFI I make an exception because the + infix "=>" syntax is widely used and intuitive.
    • +
    + +

    Specification

    + +
    +
    +(check <expr> (=> <equal>) <expected>)         MACRO
    +(check <expr> => <expected>) +
    +
    + evaluates <expr> and compares the value to the value + of <expected> using the predicate <equal>, which is + equal? when omitted. Then a report is printed according + to the current mode setting (see below) and the outcome + is recorded in a global state to be used in check-report. + The precise order of evaluation is that first <equal> + and <expected> are evaluated (in unspecified order) and + then <expr> is evaluated.
    + Example: (check (+ 1 1) => 2) +
    +
    + +
    +
    +(check-ec <qualifier>* <expr> (=> <equal>) <expected> (<argument>*))         MACRO
    +(check-ec <qualifier>
    * <expr> => <expected> (<argument>*))
    +(check-ec <qualifier>
    * <expr> (=> <equal>) <expected>)
    +(check-ec <qualifier>
    * <expr> => <expected>) +
    +
    + an eager comprehension for executing a parametric sequence of checks.

    + Enumerates the sequence of bindings specified by <qualifier>*. + For each binding evaluates <equal> and <expected> in unspecified + order. Then evalues <expr> and compares the value obtained to the + value of <expected> using the value of <equal> as predicate, which + is equal? when omitted. + The comprehension stops after the first failed check, if there + is any. Then a report is printed according to the current mode + setting (see below) and the outcome is recorded in a global state + to be used in check-report. The entire check-ec counts as a single + check.

    + In case the check fails <argument>* is used for constructing an + informative message with the argument values. Use <argument>* to + list the relevant free variables of <expr> (see examples) that you + want to have printed.

    + A <qualifier> is any qualifier of an eager comprehension as + specified in SRFI 42 [5].

    + + Examples: +

         (check-ec (: e 100) (positive? (expt 2 e)) => #t (e)) ; fails on fixnums
    +     (check-ec (: e 100) (:let x (expt 2.0 e)) (= (+ x 1) x) => #f (x)) ; fails
    +     (check-ec (: x 10) (: y 10) (: z 10)
    +               (* x (+ y z)) => (+ (* x y) (* x z))
    +               (x y z)) ; passes with 10^3 cases checked
    +   
    +
    +
    + +
    +
    +(check-report)         PROCEDURE +
    +
    + prints a summary and the first failed check, if there is any, + depending on the current mode settings. +
    +
    + +
    +
    +(check-set-mode! mode)         PROCEDURE +
    +
    + sets the current mode to mode, which must be a symbol in + '(off summary report-failed report), default is 'report. + Note that you can change the mode at any time, and that check, + check-ec and check-report use the current value. +

    + The mode symbols have the following meaning:
    + off: do not execute any of the checks,
    + summary: print only summary in (check-report) and nothing else,
    + report-failed: report failed checks when they happen, and in summary,
    + report: report every example executed.
    +

    +
    + +
    +
    +(check-reset!)         PROCEDURE +
    +
    + resets the global state (counters of correct/failed examples) + to the state immediately after loading the module for the + first time, i.e. no checks have been executed. +
    +
    + +
    +
    +(check-passed? expected-total-count)         PROCEDURE +
    +
    + #t if there were no failed checks and expected-total-count + correct checks, #f otherwise.

    + Rationale: This procedure can be used in automatized + tests by terminating a test program with the statement + (exit (if (check-passed? n) 0 1)). +

    +
    + + +

    Implementation

    + +check.scm: + implementation in R5RS + SRFI 23 (error) + SRFI 42 (comprehensions); + tested under PLT 208p1 and Scheme 48 1.3.
    + +examples.scm: + a few examples. + +

    References

    + + + +

    Copyright

    + +Copyright (C) Sebastian Egner (2005-2006). All Rights Reserved. +

    +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +

    +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +

    +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +

    +


    +
    Author: srfi-78@sebastian-egner.net
    +
    Editors: srfi-editors@srfi.schemers.org
    + + \ No newline at end of file diff --git a/doc/srfi-std/srfi-8.html b/doc/srfi-std/srfi-8.html new file mode 100644 index 0000000000..38b73406b1 --- /dev/null +++ b/doc/srfi-std/srfi-8.html @@ -0,0 +1,221 @@ + + + +SRFI 8: RECEIVE: Binding to multiple values + + + +

    Title

    + +SRFI 8: receive: Binding to multiple values + +

    Author

    + +John David Stone + +

    +Department of Mathematics and Computer Science, Grinnell College, Grinnell, Iowa 50112, +email. +

    + +

    Status

    + +This SRFI is currently in ``final'' status. To see an explanation of each status that a SRFI can hold, see here. +You can access the discussion via the archive of the mailing list. +

      +
    • Received: 1999/05/27 +
    • Draft: 1999/07/01-1999/08/30 +
    • Final: 1999/08/30 +
    + +

    Related SRFIs

    + +

    +The syntax proposed in this SRFI is used in the reference implementation of +SRFI-1, ``List library.'' +

    + +

    Abstract

    + +

    +The only mechanism that R5RS provides for binding identifiers to +the values of a multiple-valued expression is the primitive +call-with-values. This SRFI proposes a more concise, more +readable syntax for creating such bindings. +

    + +

    Rationale

    + +

    +Although R5RS supports multiple-valued expressions, it provides +only the essential procedures values and +call-with-values. It is evident that the authors expected +Scheme programmers to define other constructs in terms of these, +abstracting common patterns of use. +

    + +

    +One such pattern consists in binding an identifier to each of the values of +a multiple-valued expression and then evaluating an expression in the scope +of the bindings. As an instance of this pattern, consider the following +excerpt from a quicksort procedure: +

    + +
    +
    +(call-with-values
    +  (lambda ()
    +    (partition (precedes pivot) others))
    +  (lambda (fore aft)
    +    (append (qsort fore) (cons pivot (qsort aft)))))
    +
    +
    + +

    +Here partition is a multiple-valued procedure that takes two +arguments, a predicate and a list, and returns two lists, one comprising +the list elements that satisfy the predicate, the other those that do not. +The purpose of the expression shown is to partition the list +others, sort each of the sublists, and recombine the results +into a sorted list. +

    + +

    +For our purposes, the important step is the binding of the identifiers +fore and aft to the values returned by +partition. Expressing the construction and use of these +bindings with the call-by-values primitive is cumbersome: One +must explicitly embed the expression that provides the values for the +bindings in a parameterless procedure, and one must explicitly embed the +expression to be evaluated in the scope of those bindings in another +procedure, writing as its parameters the identifiers that are to be bound +to the values received. +

    + +

    +These embeddings are boilerplate, exposing the underlying binding mechanism +but not revealing anything relevant to the particular program in which it +occurs. So the use of a syntactic abstraction that exposes only the +interesting parts -- the identifiers to be bound, the multiple-valued +expression that supplies the values, and the body of the receiving +procedure -- makes the code more concise and more readable: +

    + +
    +
    +(receive (fore aft) (partition (precedes pivot) others)
    +  (append (qsort fore) (cons pivot (qsort aft))))
    +
    +
    + +

    +The advantages are similar to those of a let-expression over a +procedure call with a lambda-expression as its operator. In +both cases, cleanly separating a ``header'' in which the bindings are +established from a ``body'' in which they are used makes it easier to +follow the code. +

    + +

    Specification

    + +

    +(receive <formals> <expression> +<body>)     library syntax +

    + +

    +<Formals>, <expression>, and <body> are as described in +R5RS. Specifically, <formals> can have any of three +forms: +

    + +
      +
    • +(<variable1> ... +<variablen>): The environment in which +the receive-expression is evaluated is extended by binding +<variable1>, ..., +<variablen> to fresh locations. The +<expression> is evaluated, and its values are stored into those +locations. (It is an error if <expression> does not have exactly +n values.) +

    • + +
    • +<variable>: The environment in which the +receive-expression is evaluated is extended by binding +<variable> to a fresh location. The <expression> is evaluated, +its values are converted into a newly allocated list, and the list is +stored in the location bound to <variable>. +

    • + +
    • +(<variable1> ... +<variablen> . <variablen + +1>): The environment in which +the receive-expression is evaluated is extended by binding +<variable1>, ..., +<variablen + 1> to fresh locations. The +<expression> is evaluated. Its first n values are stored into +the locations bound to <variable1> ... +<variablen>. Any remaining values are converted +into a newly allocated list, which is stored into the location bound to +<variablen + 1>. (It is an error if +<expression> does not have at least +n values.) +

    • +
    + +

    +In any case, the expressions in <body> are evaluated sequentially in +the extended environment. The results of the last expression in the body +are the values of the receive-expression. +

    + +

    Reference implementation

    + +
    +
    +(define-syntax receive
    +  (syntax-rules ()
    +    ((receive formals expression body ...)
    +     (call-with-values (lambda () expression)
    +                       (lambda formals body ...)))))
    +
    +
    + +
    + +

    +Copyright (C) John David Stone (1999). All Rights Reserved. +

    + +

    +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: +

    +

    +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. +

    +

    +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +

    + +
    + +
    Editor: Mike Sperber
    + + + diff --git a/doc/srfi-std/srfi-86.html b/doc/srfi-std/srfi-86.html new file mode 100644 index 0000000000..39255f2c0b --- /dev/null +++ b/doc/srfi-std/srfi-86.html @@ -0,0 +1,1937 @@ + +SRFI 86: MU and NU simulating VALUES & CALL-WITH-VALUES, and their related LET-syntax + +

    Title

    + +MU and NU simulating VALUES & CALL-WITH-VALUES, and their related LET-syntax + +

    Author

    + +Joo ChurlSoo + +

    Status

    + +This SRFI is currently in ``final'' status. To see an explanation of each +status that a SRFI can hold, see +here. +To +provide input on this SRFI, please +mailto:srfi minus 86 at srfi dot schemers dot org. +See instructions +here to subscribe to the list. You can access the discussion via +the +archive of the mailing list. +You can access +post-finalization messages via + +the archive of the mailing list. +

    +

    + +

    Abstract

    + +

    +Unlike the values/call-with-values mechanism of +R5RS, this SRFI uses an explicit representation for multiple return +values as a single value, namely a procedure. Decomposition of +multiple values is done by simple application. Each of the two +macros, mu and nu, evaluates to a procedure +that takes one procedure argument. The mu and +nu can be compared with lambda. While +lambda expression that consists of <formals> and <body> +requires some actual arguments later when the evaluated +lambda expression is called, mu and +nu expressions that consist of <expression>s +corresponding to actual arguments of lambda require +<formals> and <body>, that is, an evaluated lambda +expression, later when the evaluated mu and +nu expressions are called. +

    + +

    +This SRFI also introduces new let-syntax depending on +mu and nu to manipulate multiple values, +alet and alet* that are compatible with +let and let* of R5RS in single value +bindings. They also have a binding form making use of +values and call-with-values to handle +multiple values. In addition, they have several new binding forms for +useful functions such as escape, recursion, etc. +

    + +

    Rationale

    + +

    +It is impossible to bind the evaluated result of values +expression to a single variable unlike other Scheme expressions. +Moreover, the pair of values and +call-with-values is clumsy to use and somewhat slow under +some circumstances. A solution would be to enclose the arguments of +values expression in a procedure of one argument, a +consumer procedure of call-with-values. The following are examples to +show the differences. +

    + +
    (define v (values 1 2 3))		=> error
    +(define v (lambda () (values 1 2 3)))	=> (lambda () (values 1 2 3))
    +(define m (mu 1 2 3))			=> (lambda (f) (f 1 2 3))
    +(define a (apply values 1 '(2 3)))	=> error
    +(define a
    +  (lambda () (apply values 1 '(2 3))))	=> (lambda () (apply values 1 '(2 3)))
    +(define n (nu 1 '(2 3)))		=> (lambda (f) (apply f 1 '(2 3)))
    +
    +(call-with-values v list)	=> (1 2 3)
    +(m list)			=> (1 2 3)
    +(call-with-values a list)	=> (1 2 3)
    +(n list)			=> (1 2 3)
    +
    + +

    +The alet and alet* are cases in point to use +mu and nu. The differences between this +let-syntax and others, and some additional functions are +best explained by simple examples. +

    + +
      +
    1. + +

      The following are rest argument forms of each SRFI.

      + +

      In SRFI 11:

      + +
      (let-values ((a (values 1 2)) ((b c) (values 3 4)))
      +	    (list a b c))
      +=> ((1 2) 3 4)
      +
      + +

      In SRFI 71:

      + +
      (srfi-let (((values . a) (values 1 2)) ((values b c) (values 3 4)))
      +	  (list a b c))
      +=> ((1 2) 3 4)
      +
      + +

      In this SRFI:

      + +
      (alet (a (mu 1 2) ((b c) (mu 3 4)))
      +  (list a b c))
      +=> ((1 2) 3 4)
      +
      + +
    2. + +
    3. The expressions for alet bindings are evaluated in +sequence from left to right unlike let of R5RS and +let of SRFI 71.

      + +

      In SRFI 71:

      + +
      (srfi-let ((a (begin (display "1st") 1))
      +	   (b c (values (begin (display "2nd") 2) 3))
      +	   (d (begin (display "3rd") 4))
      +	   ((values e . f) (values (begin (display "4th") 5) 6)))
      +	  (list a b c d e f))
      +=> 2nd4th1st3rd(1 2 3 4 5 (6))
      +
      + +

      In this SRFI:

      + +
      (alet ((a (begin (display "1st") 1))
      +       (b c (mu (begin (display "2nd") 2) 3))
      +       (d (begin (display "3rd") 4))
      +       ((e . f) (mu (begin (display "4th") 5) 6)))
      +  (list a b c d e f))
      +=> 1st2nd3rd4th(1 2 3 4 5 (6))
      +
      +
    4. + +
    5. The bindings that require multiple values can take multiple expressions, if + syntactically possible, as well as a single expression that produce + multiple values.

      + +
      (alet* (((a b) (mu 1 2))
      +	((c d e) a (+ a b c) (+ a b c d))
      +	((f . g) (mu 5 6 7))
      +	((h i j . k) e 9 10 h i j))
      +  (list a b c d e f g h i j k))
      +
      +=> (1 2 1 4 8 5 (6 7) 8 9 10 (8 9 10))
      +
      +
    6. + +
    7. The named-alet and named-alet* are +allowed to take multiple values bindings.

      + +

      In SRFI 71:

      + +
      (srfi-let tag ((a 1) (b 2) (c 3) (d 4) (e 5))
      +	  (if (< a 10) (tag 10 b c d e) (list a b c d e)))
      +=> (10 2 3 4 5)
      +
      + +

      In this SRFI:

      + +
      (alet* tag ((a 1) (a b b c (mu (+ a 2) 4 5 6)) ((d e e) b 5 (+ a b c)))
      +       (if (< a 10) (tag a 10 b c c d e d) (list a b c d e)))
      +=> (10 6 6 5 5)
      +
      +
    8. + +
    9. They have a new binding form that has a recursive function like +named-alet. It is also allowed to take multiple values +bindings.

      + +
      (alet* ((a 1)
      +	((b 2) (b c c (mu 3 4 5)) ((d e d (mu a b c)) . intag) . tag)
      +	(f 6))
      +  (if (< d 10)
      +      (intag d e 10)
      +      (if (< c 10)
      +	  (tag b 11 c 12 a b d intag)
      +	  (list a b c d e f))))
      +=> (1 11 12 10 3 6)
      +
      +
    10. + +
    11. They have a new binding form that has an escape function.

      + +
      (alet ((exit)
      +       (a (begin (display "1st") 1))
      +       (b c (mu (begin (display "2nd") 2) (begin (display "3rd") 3))))
      +  (display (list a b c))
      +  (exit 10)
      +  (display "end"))
      +=> 1st2nd3rd(1 2 3)10
      +
      +
    12. + +
    13. The and-let and and-let* are +integrated into the alet and alet* with a + syntactic keyword and.

      + +
      (alet ((and (a (begin (display "1st") 1))
      +	    (b (begin (display "2nd") 2))
      +	    (c (begin (display "false") #f))
      +	    (d (begin (display "3nd") 3))))
      +  (list a b c d))
      +=> 1st2ndfalse#f
      +
      +(alet ((and (a (begin (display "1st") 1))
      +	    (b (begin (display "2nd") 2) (< b 2)) ; different from SRFI 2
      +	    (c (begin (display "false") #f))
      +	    (d (begin (display "3nd") 3))))
      +  (list a b c d))
      +=> 1st2nd#f
      +
      +
    14. + +
    15. The rest-values of SRFI 51 is integrated into the +alet and alet* with + syntactic keywords opt and cat in the + similar way to let-optionals in Scsh.

      + +
      ((lambda (str . rest)
      +   (alet* ((len (string-length str))
      +	   (opt rest
      +		(start 0 (integer? start)
      +		       (if (< start 0) 0 (if (< len start) len start)))	 ;true
      +		(end len (integer? end)
      +		     (if (< end start) start (if (< len end) len end)))));true
      +     (substring str start end))) "abcdefg" 1 20)
      +=> "bcdefg"
      +
      +((lambda (str . rest)
      +   (alet* ((len (string-length str))
      +	   (min (apply min rest))
      +	   (cat rest
      +		(start 0 (= start min)
      +		       (if (< start 0) 0 (if (< len start) len start)))	 ;true
      +		(end len (integer? end)
      +		     (if (< end start) start (if (< len end) len end)))));true
      +     (substring str start end))) "abcdefg" 20 1)
      +=> "bcdefg"
      +
      +((lambda (str . rest)
      +   (alet ((cat rest
      +	       (start 0
      +		      (and (list? start) (= 2 (length start))
      +			   (eq? 'start (car start)))
      +		      (cadr start))	; true
      +	       (end (string-length str)
      +		    (and (list? end) (= 2 (length end)) (eq? 'end (car end)))
      +		    (cadr end))))	; true
      +     (substring str start end))) "abcdefg" '(end 6) '(start 1))
      +=> "bcdef"
      +
      +
    16. + +
    17. The let-keywords and let-keywords* + are integrated into the alet and + alet* with a syntactic keyword key. + They use any Scheme objects as keywords. + +

      (define rest-list '(a 10 cc 30 40 b 20))
      +(alet ((key rest-list (a 1) (b 2) ((c 'cc) 3) . d)) (list a b c d))
      +=> (10 2 30 (40 b 20))
      +
      +(alet ((key rest-list (a 1) (b 2) ((c 'cc) 3) #f . d)) (list a b c d))
      +=> (10 2 30 (40 b 20))
      +
      +(alet ((key rest-list (a 1) (b 2) ((c 'cc) 3) #t . d)) (list a b c d))
      +=> (10 20 30 (40))
      +
      +(define rest (list 'a 10 'd 40 "c" 30 50 'b 20))
      +(alet ((key rest (a 1) (b 2) ((c "c") 3) . d)) (list a b c d))
      +=> (10 2 30 (d 40 50 b 20))
      +
      +(alet ((key rest (a 1) (b 2) ((c "c") 3) #f . d)) (list a b c d))
      +=> (10 2 3 (d 40 "c" 30 50 b 20))
      +
      +(alet ((key rest (a 1) (b 2) ((c "c") 3) #t . d)) (list a b c d))
      +=> (10 20 30 (d 40 50))
      +
      +((lambda (m . n)
      +   (alet* ((opt n (a 10) (b 20) (c 30) . d)
      +	   (key d (x 100) (y 200) (a 300)))
      +     (list m a b c x y)))
      + 0 1 2 3 'a 30 'y 20 'x 10)
      +=> (0 30 2 3 10 20)
      +
      +((lambda (m . n)
      +   (alet* ((key n (x 100) (y 200) (a 300) . d)
      +	   (opt d (a 10) (b 20) (c 30)))
      +     (list m a b c x y)))
      + 0 'a 30 'y 20 'x 10 1 2 3)
      +=> (0 1 2 3 10 20)
      +
      +
    18. + +
    19. The letrecand letrec* are integrated +into the alet and alet* with a + syntactic keyword rec.

      + +
      (alet* ((a 1)
      +	(rec (a 2) (b 3) (b (lambda () c)) (c a))
      +	(d 50))
      +  (list a (b) c d))
      +=> '(2 2 2 50)
      +
      +
    20. + +
    21. They have a binding form that use call-with-values +and values to handle multiple values with a syntactic +keyword values like SRFI 71.

      + +
      (alet ((a b (mu 1 2))
      +       (values c d (values 3 4))	;This is different from SRFI 71.
      +       ((e f) (mu 5 6))
      +       ((values g h) (values 7 8))
      +       ((i j . k) (nu 9 '(10 11 12)))
      +       ((values l m . n) (apply values 13 '(14 15 16)))
      +       o (mu 17 18)
      +       ((values . p) (values 19 20)))
      +  (list a b c d e f g h i j k l m n o p))
      +=> (1 2 3 4 5 6 7 8 9 10 (11 12) 13 14 (15 16) (17 18) (19 20))
      +
      +
    22. + +
    23. They have a new binding form that works as an intervening external + environment in alet and as an intervening internal + environment in alet*.

      + +
      (alet ((a 1)
      +       (() (define a 10) (define b 100))
      +       (b a))
      +  (list a b))
      +=> (1 10)
      +
      +(alet* ((a 1)
      +	(() (define a 10) (define b 100))
      +	(b a))
      +  (list a b))
      +=> (10 10)
      +
      +
    24. +
    + +

    Specification

    + +
    (mu <expr> ...)			=> (lambda (f) (f <expr> ...))
    +(nu <expr> ... <exprn>)		=> (lambda (f) (apply f <expr> ... <exprn>))
    +
    + +

    The <exprn> should be a list.

    + +

    +Each macro evaluates to a procedure of one argument. The environment +in effect when the macro expression was evaluated is remembered as +part of the procedure. When the procedure is later called with an +actual argument, a procedure, the environment in which the macro was +evaluated is extended by binding <expr>s to the corresponding +variables in the formal argument list of the argument procedure. The +argument procedure of mu is called with the <expr>s, +and that of nu is applied to APPLY procedure with the +<expr>s.

    + +
    (alet  (<binding spec> ...) body ...)
    +(alet* (<binding spec> ...) body ...)
    +
    + +

    + syntax-rules identifier: opt + cat key and + rec values +

    + +

    <binding spec>:

    + +
      +
    1. (<var> <expr>)
    2. +
    3. (<var1> <var2> <var3> ... <expr>)
    4. +
    5. ((<var>) <expr>)
    6. +
    7. ((<var1> <var2> <var3> ... ) <expr>)
    8. +
    9. ((<var1> ... <varm> . <varn>) <expr>)
    10. +
    11. ((<var1> <var2> <var3> ... ) <expr1> <expr2> <expr3> ...)
    12. +
    13. ((<var1> ... <varm> . <varn>) <expr1> ... <exprm> <exprn> ...)
    14. +
    15. <var> <expr>	
    16. +
    17. (<var>)	
    18. +
    19. (<binding spec1> <binding spec2> ... . <var>)
    20. +
    21. (() . <var>)
    22. +
    23. (and (<var1> <expr1> [<test1>]) (<var2> <expr2> [<test2>]) ...)
    24. +
    25. (opt <rest list>
      +    (<var1> <default1> [<test1> [<true substitute1> [<false substitute1>]]])
      +    ...
      +    (<varn> <defaultn> [<testn> [<true substituten> [<false substituten>]]])
      +    . [<rest var>])
    26. +
    27. (cat <rest list>
      +     (<var1> <default1> [<test1> [<true substitute1> [<false substitute1>]]])
      +     ...
      +     (<varn> <defaultn> [<testn> [<true substituten> [<false substituten>]]])
      +     . [<rest var>])
    28. +
    29. (key <rest list>
      + (<var spec1> <default1> [<test1> [<true substitute1> [<false substitute1>]]])
      + ...
      + (<var specn> <defaultn> [<testn> [<true substituten> [<false substituten>]]])
      + [<option>]
      + . [<rest var>])
    30. +
    31. (rec (<var1> <expr1>) (<var2> <expr2>) ...)
    32. +
    33. (values <var1> <var2> ... <expr>)
    34. +
    35. ((values <var1> <var2> ...) <expr>)
    36. +
    37. ((values <var1> ... . <varn>) <expr>) 
    38. +
    39. ((values <var1> <var2> <var3> ...) <expr1> <expr2> <expr3> ...)
    40. +
    41. ((values <var1> ... . <varn>) <expr1> ... <exprn> ...) 
    42. +
    43. (() <expr1> <expr2> ...)
    44. +
    + +

    +The alet* is to the alet what the +let* is to the let. However, the <binding +spec>s of alet are evaluated in sequence from left to +spec>right unlike let of +R5RS. The alet and alet* make use of +mu or nu instead of values to +handle multiple values. So, the single <expr> of multiple values +binding should be a mu or nu expression, or +its equivalent. And the number of arguments of mu or the +number of `applied' arguments of nu must match the number +of values expected by the binding specification. Otherwise an error +is signaled, as lambda expression would. +

    + +
      +
    1. (<var> <expr>)
      +This is the same as let (R5RS, 4.2.2). +
    2. + +
    3. (<var1> <var2> <var3> ... <expr>)
      +This is the same as 4.
    4. + +
    5. ((<var>) <expr>)
      +This is the same as 1.
    6. + +
    7. ((<var1> <var2> <var3> ... ) <expr>)
    8. +
    9. ((<var1> ... <varm> . <varn>) <expr>)
      + +The <expr> must be a mu or nu +expression or its equivalent. The matching of <var>s to the +values of <expr> is as for the matching of <formals> to +arguments in a lambda expression (R5RS, 4.1.4). +
    10. + +
    11. ((<var1> <var2> <var3> ... ) <expr1> <expr2> <expr3> ...)
      +This is the same as +
      (let[*] ((<var1> <expr1>) (<var2> <expr2>) (<var3> <expr3>) ...).
      +
    12. + +
    13. ((<var1> ... <varm> . <varn>) <expr1> ... <exprm> <exprn> ...) 
      +This is the same as +
      (let[*] ((<var1> <expr1>) ... (<varm> <exprm>) (<varn>  (list <exprn> ...))).
      +
    14. + +
    15. <var> <expr>
      +The <var> is a rest argument, so the <expr> should be a form that can deliver +multiple values, that is, a mu or nu expression or its equivalent. +
    16. + +
    17. (<var>)
      +The <var> becomes an escape procedure that can take return +values of alet[*] as its arguments. + +
    18. (<binding spec1> <binding spec2> ... . <var>)
      +The <var> becomes a recursive procedure that takes all <vars> of <binding +spec>s as arguments. +
    19. + +
    20. (() . <var>)
      +The <var> becomes a recursive thunk that takes no argument. +
    21. + +
    22. (and (<var1> <expr1> [<test1>]) (<var2> <expr2> [<test2>]) ...)
      + +Each <expr> is evaluated sequentially and bound to the +corresponding <var>. During the process, if there is no +<test> and the value of <expr> is false, it stops and +returns #f. When there is a <test>, the process is continued +regardless of the value of <expr> unless the value of <test> is +false. If the value of <test> is false, it stops and returns #f. +
    23. + +
    24. +
      (opt <rest list>
      +    (<var1> <default1> [<test1> [<true substitute1> [<false substitute1>]]])
      +    ...
      +    (<varn> <defaultn> [<testn> [<true substituten> [<false substituten>]]])
      +    . [<rest var>])
      +
      + +This binds each <var> to a corresponding element of <rest list>. +If there is no more element, then the corresponding <default> is +evaluated and bound to the <var>. An error is signaled when +there are more elements than <var>s. But if <rest var> is +given, it is bound to the remaining elements. If there is a +<test>, it is evaluated only when <var> is bound to an +element of <rest list>. If it returns a false value and there is no +<false substitute>, an error is signaled. If it returns a false value +and there is a <false +substitute>, <var> is rebound to the result of evaluating <false substitute> +instead of signaling an error. If it returns a true value and there is a +<true substitute>, <var> is rebound to the result of evaluating <true +substitute>. +
    25. + +
    26. (cat <rest list>
      +     (<var1> <default1> [<test1> [<true substitute1> [<false substitute1>]]])
      +     ...
      +     (<varn> <defaultn> [<testn> [<true substituten> [<false substituten>]]])
      +     . [<rest var>])
      +
      + +This is the same as the above opt except the binding +method. It temporarily binds <var> to each elements of <rest +list> sequentally, until <test> returns a true value, then +the <var> is finally bound to the passed element. If there is +no <test>, the first element of the remained <rest list> +is regarded as passing. If any element of the <rest list> does +not pass the <test>, the <default> is bound to the +<var> instead of signaling an error. If there is a <false +substitute> and <test> returns a false value, <var> is +finally bound to the result of evaluating <false substitute> +instead of the above process. If there is a <true substitute> +and <test> returns a true value, <var> is finally bound to +the result of evaluating <true substitute>. +
    27. + +
    28. (key <rest list>
      + (<var spec1> <default1> [<test1> [<true substitute1> [<false substitute1>]]])
      + ...
      + (<var specn> <defaultn> [<testn> [<true substituten> [<false substituten>]]])
      + [<option>]
      + . [<rest var>])
      +
      +<var spec> --> <var> | (<var> <keyword>)
      +<option> --> #f | #t
      +<keyword>  --> <any scheme object>
      +<default> --> <expression>
      +<test> --> <expression>
      +<true substitute> --> <expression>
      +<false substitute> --> <expression>
      +
      + +This key form is the same as the cat form in view of the fact that both +don't use argument position for binding <var>s to elements of <rest list>. +However, for extracting values from <rest list>, the former uses explicitly +keywords and the latter uses implicitly <test>s. The keywords in this form +are not self-evaluating symbols (keyword objects) but any scheme objects. The +keyword used in <rest list> for the corresponding variable is a symbol of the +same name as the variable of the <var spec> composed of a single <var>. But +the keyword can be any scheme object when the <var spec> is specified as a +parenthesized variable and a keyword. +The elements of <rest list> are sequentially interpreted as a series of pairs, +where the first member of each pair is a keyword and the second is the +corresponding value. If there is no element for a particular keyword, the +<var> is bound to the result of evaluating <default>. When there is a <test>, +it is evaluated only when <var> is bound to an element of <rest list>. If it +returns a false value and there is no <false substitute>, an error is +signaled. If it returns a false value and there is a <false substitute>, +<var> is rebound to the result of evaluating <false substitute> instead of +signaling an error. If it returns a true value and there is a <true +substitute>, <var> is rebound to the result of evaluating <true substitute>. +When there are more elements than ones that are specified by <var spec>s, an +error is signaled. But if <rest var> is given, it is bound to the remaining +elements. +The following options can be used to control binding behavior when the keyword +of keyword-value pair at the bind processing site is different from any +keywords specified by <var spec>s. + +
        +
      1. default -- the remaining elements of <rest list> are continually + interpreted as a series of pairs.
      2. +
      3. #f - the variable is bound to the corresponding <default>.
      4. +
      5. #t - the remaining elements of <rest list> are +continually interpreted as a single element until the element is a +particular keyword.
      6. +
      +
    29. + +
    30. (rec (<var1> <expr1>) (<var2>
      +<expr2>) ...)
      + +This is the same as
      (letrec[*] ((<var1> <expr1>) (<var2> <expr2>) ...)
      +
    31. + +
    32. (values <var1> <var2> ... <expr>)
      + +This is the same as 17. +
    33. + +
    34. ((values <var1> <var2> ...) <expr>)
    35. + +
    36. ((values <var1> ... . <varn>) <expr>)
      + +The <expr> should be a values expression or its +equivalent. The matching of <var>s to the values of +<expr> is as for the matching of <formals> to arguments in a +lambda expression. +
    37. + +
    38. ((values <var1> <var2> <var3> ...) <expr1> <expr2> <expr3> ...)
      + +This is the same as +
      (let[*] ((<var1> <expr1>) (<var2> <expr2>) (<var3> <expr3>) ...)
      +
    39. + +
    40.  ((values <var1> ... . <varn>) <expr1> ... <exprn> ...) 
      + +This is the same as (let[*] ((<var1> <expr1>) +... (<varn> (list <exprn> ...))). +
    41. + +
    42. (() <expr1> <expr2> ...)
      + +This works as an intervening external environment in +alet, and an intervening internal environment in +alet*. +
    43. +
    + +
    (alet  name (<binding spec> ...) body ...)
    +(alet* name (<binding spec> ...) body ...)
    +
    + +

    +These are the same as the named-let (R5RS, 4.2.4) except +binding specification. These allow all sorts of bindings in <binding +spec>.

    + +

    Examples

    + +
    (alet ((a (begin (display "1st") 1))
    +       ((b c) 2 (begin (display "2nd") 3))
    +       (() (define m #f) (define n (list 8)))
    +       ((d (begin (display "3rd") 4))
    +	(key '(e 5 tmp 6) (e 0) ((f 'tmp) 55)) . p)
    +       g (nu (begin (display "4th") 7) n)
    +       ((values . h) (apply values 7 (begin (display "5th") n)))
    +       ((m 11) (n n) . q)
    +       (rec (i (lambda () (- (j) 1)))
    +	    (j (lambda ()  10)))
    +       (and (k (begin (display "6th") m))
    +	    (l (begin (display "end") (newline) 12)))
    +       (o))
    +  (if (< d 10)
    +      (p 40 50 60)
    +      (if (< m 100)
    +	  (q 111 n)
    +	  (begin (display (list a b c d e f g h (i) (j) k l m n))
    +		 (newline))))
    +  (o (list o p q))
    +  (display "This is not displayed"))
    +=> 1st2nd3rd4th5th6th#f
    +
    +(alet* ((a (begin (display "1st") 1))
    +	((b c) 2 (begin (display "2nd") 3))
    +	(() (define m #f) (define n (list 8)))
    +	((d (begin (display "3rd") 4))
    +	 (key '(e 5 tmp 6) (e 0) ((f 'tmp) 55)) . p)
    +	g (nu (begin (display "4th") 7) n)
    +	((values . h) (apply values 7 (begin (display "5th") n)))
    +	((m 11) (n n) . q)
    +	(rec (i (lambda () (- (j) 1)))
    +	     (j (lambda ()  10)))
    +	(and (k (begin (display "6th") m))
    +	     (l (begin (display "end") (newline) 12)))
    +	(o))
    +  (if (< d 10)
    +      (p 40 50 60)
    +      (if (< m 100)
    +	  (q 111 n)
    +	  (begin (display (list a b c d e f g h (i) (j) k l m n))
    +		 (newline))))
    +  (o (list o p q))
    +  (display "This is not displayed"))
    +=> 1st2nd3rd4th5th6thend
    +   4th5th6thend
    +   6thend
    +   (1 2 3 40 50 60 (7 8) (7 8) 9 10 111 12 111 (8))
    +   (#<continuation> #<procedure:p> #<procedure:q>)
    +
    +(define (arg-message head-message proc . message)
    +  (display head-message) (newline)
    +  (alet ((() . lp)
    +	 (() (for-each display message))
    +	 (arg (read)))
    +    (if (proc arg) arg (lp))))
    +
    +(define (substr str . rest)
    +  (alet* ((len (string-length str))
    +	  (opt rest
    +	       (start 0
    +		      (and (integer? start) (<= 0 start len))
    +		      start
    +		      (arg-message
    +		       "The first  argument:"
    +		       (lambda (n) (and (integer? n) (<= 0 n len)))
    +		       "Write number (" 0 " <= number <= " len "): "))
    +	       (end len
    +		    (and (integer? end) (<= start end len))
    +		    end
    +		    (arg-message
    +		     "The second argument:"
    +		     (lambda (n) (and (integer? n) (<= start n len)))
    +		     "Write number (" start " <= number <= " len "): "))))
    +    (substring str start end)))
    +	
    +(substr "abcdefghi" 3)
    +=> "defghi"
    +
    +(substr "abcdefghi" 3 7)
    +=> "defg"
    +
    +(substr "abcdefghi" 20 7)
    +=> The first  argument:
    +   Write number (0 <= number <= 9): 3
    +   "defg"
    +
    +(substr "abcdefghi" "a" 20)
    +=> The first  argument:
    +   Write number (0 <= number <= 9): 2
    +   The second argument:
    +   Write number (2 <= number <= 9): 10
    +   Write number (2 <= number <= 9): 9
    +   "cdefghi"
    +
    + +

    Implementation

    + +

    +The following implementation is written in R5RS hygienic macros and +requires SRFI 23 (Error reporting mechanism). +

    + +
    ;;; mu & nu
    +(define-syntax mu
    +  (syntax-rules ()
    +    ((mu argument ...)
    +     (lambda (f) (f argument ...)))))
    +
    +(define-syntax nu
    +  (syntax-rules ()
    +    ((nu argument ...)
    +     (lambda (f) (apply f argument ...)))))
    +
    +;;; alet
    +(define-syntax alet
    +  (syntax-rules ()
    +    ((alet (bn ...) bd ...)
    +     (%alet () () (bn ...) bd ...))
    +    ((alet var (bn ...) bd ...)
    +     (%alet (var) () (bn ...) bd ...))))
    +
    +(define-syntax %alet
    +  (syntax-rules (opt cat key rec and values)
    +    ((%alet () ((n v) ...) () bd ...)
    +     ((lambda (n ...) bd ...) v ...))
    +    ((%alet (var) ((n v) ...) () bd ...)
    +     ((letrec ((var (lambda (n ...) bd ...)))
    +	var) v ...))
    +    ((%alet (var (p ...) (nv ...) (bn ...)) ((n v) ...) () bd ...)
    +     ((letrec ((t (lambda (v ...)
    +		    (%alet (p ...) (nv ... (n v) ... (var t))
    +			   (bn ...) bd ...))))
    +	t) v ...))
    +    ((%alet (p ...) (nv ...) ((() a b ...) bn ...) bd ...)
    +     ((lambda () a b ... (%alet (p ...) (nv ...) (bn ...) bd ...))))
    +    ((%alet (p ...) (nv ...) (((a) c) bn ...) bd ...)
    +     ((lambda (t) (%alet (p ...) (nv ... (a t)) (bn ...) bd ...)) c))
    +
    +    ((%alet (p ...) (nv ...) (((values a) c) bn ...) bd ...)
    +     ((lambda (t) (%alet (p ...) (nv ... (a t)) (bn ...) bd ...)) c))
    +    ((%alet (p ...) (nv ...) (((values . b) c d ...) bn ...) bd ...)
    +     (%alet "dot" (p ...) (nv ...) (values) (b c d ...) (bn ...) bd ...))
    +    ((%alet "dot" (p ...) (nv ...) (values t ...) ((a . b) c ...)
    +	    (bn ...) bd ...)
    +     (%alet "dot" (p ...) (nv ... (a tn)) (values t ... tn) (b c ...)
    +	    (bn ...) bd ...))
    +    ((%alet "dot" (p ...) (nv ...) (values t ...) (() c) (bn ...) bd ...)
    +     (call-with-values (lambda () c)
    +       (lambda (t ...) (%alet (p ...) (nv ...) (bn ...) bd ...))))
    +    ((%alet "dot" (p ...) (nv ...) (values t ...) (() c ...) (bn ...) bd ...)
    +     ((lambda (t ...) (%alet (p ...) (nv ...) (bn ...) bd ...)) c ...))
    +    ((%alet "dot" (p ...) (nv ...) (values t ...) (b c) (bn ...) bd ...)
    +     (call-with-values (lambda () c)
    +       (lambda (t ... . tn)
    +	 (%alet (p ...) (nv ... (b tn)) (bn ...) bd ...))))
    +    ((%alet "dot" (p ...) (nv ...) (values t ...) (b c ...) (bn ...) bd ...)
    +     ((lambda (t ... . tn)
    +	(%alet (p ...) (nv ... (b tn)) (bn ...) bd ...)) c ...))
    +
    +    ((%alet (p ...) (nv ...) (((a . b) c d ...) bn ...) bd ...)
    +     (%alet "dot" (p ...) (nv ... (a t)) (t) (b c d ...) (bn ...) bd ...))
    +    ((%alet "dot" (p ...) (nv ...) (t ...) ((a . b) c ...) (bn ...) bd ...)
    +     (%alet "dot" (p ...) (nv ... (a tn)) (t ... tn) (b c ...) (bn ...)
    +	    bd ...))
    +    ((%alet "dot" (p ...) (nv ...) (t ...) (() c) (bn ...) bd ...)
    +     (c (lambda (t ...) (%alet (p ...) (nv ...) (bn ...) bd ...))))
    +    ((%alet "dot" (p ...) (nv ...) (t ...) (() c ...) (bn ...) bd ...)
    +     ((lambda (t ...) (%alet (p ...) (nv ...) (bn ...) bd ...)) c ...))
    +    ((%alet "dot" (p ...) (nv ...) (t ...) (b c) (bn ...) bd ...)
    +     (c (lambda (t ... . tn) (%alet (p ...) (nv ... (b tn)) (bn ...) bd ...))))
    +    ((%alet "dot" (p ...) (nv ...) (t ...) (b c ...) (bn ...) bd ...)
    +     ((lambda (t ... . tn)
    +	(%alet (p ...) (nv ... (b tn)) (bn ...) bd ...)) c ...))
    +
    +    ((%alet (p ...) (nv ...) ((and (n1 v1 t1 ...) (n2 v2 t2 ...) ...) bn ...)
    +	    bd ...)
    +     (%alet "and" (p ...) (nv ...) ((n1 v1 t1 ...) (n2 v2 t2 ...) ...) (bn ...)
    +	    bd ...))
    +    ((%alet "and" (p ...) (nv ...) ((n v) nvt ...) (bn ...) bd ...)
    +     (let ((t v))
    +       (and t (%alet "and" (p ...) (nv ... (n t)) (nvt ...) (bn ...) bd ...))))
    +    ((%alet "and" (p ...) (nv ...) ((n v t) nvt ...) (bn ...) bd ...)
    +     (let ((tt v))
    +       (and (let ((n tt)) t)
    +	    (%alet "and" (p ...) (nv ... (n tt)) (nvt ...) (bn ...) bd ...)))) 
    +    ((%alet "and" (p ...) (nv ...) () (bn ...) bd ...)
    +     (%alet (p ...) (nv ...) (bn ...) bd ...))
    +    ((%alet (p ...) (nv ...) ((opt z a . e) bn ...) bd ...)
    +     (%alet "opt" (p ...) (nv ...) z (a . e) (bn ...) bd ...))
    +    ((%alet "opt" (p ...) (nv ...) z ((n d t ...)) (bn ...) bd ...)
    +     (let ((x (if (null? z)
    +		  d
    +		  (if (null? (cdr z))
    +		      (wow-opt n (car z) t ...)
    +		      (error "alet: too many arguments" (cdr z))))))
    +       (%alet (p ...) (nv ... (n x)) (bn ...) bd ...)))
    +    ((%alet "opt" (p ...) (nv ...) z ((n d t ...) . e) (bn ...) bd ...)
    +     (let ((y (if (null? z) z (cdr z)))
    +	   (x (if (null? z)
    +		  d
    +		  (wow-opt n (car z) t ...))))
    +       (%alet "opt" (p ...) (nv ... (n x)) y e (bn ...) bd ...)))
    +    ((%alet "opt" (p ...) (nv ...) z e (bn ...) bd ...)
    +     (let ((te z))
    +       (%alet (p ...) (nv ... (e te)) (bn ...) bd ...)))
    +    ((%alet (p ...) (nv ...) ((cat z a . e) bn ...) bd ...)
    +     (let ((y z))
    +       (%alet "cat" (p ...) (nv ...) y (a . e) (bn ...) bd ...)))
    +    ((%alet "cat" (p ...) (nv ...) z ((n d t ...)) (bn ...) bd ...)
    +     (let ((x (if (null? z)
    +		  d
    +		  (if (null? (cdr z))
    +		      (wow-cat-end z n t ...)
    +		      (error "alet: too many arguments" (cdr z))))))
    +       (%alet (p ...) (nv ... (n x)) (bn ...) bd ...)))
    +    ((%alet "cat" (p ...) (nv ...) z ((n d t ...) . e) (bn ...) bd ...)
    +     (let ((x (if (null? z)
    +		  d
    +		  (wow-cat! z n d t ...))))
    +       (%alet "cat" (p ...) (nv ... (n x)) z e (bn ...) bd ...)))
    +    ((%alet "cat" (p ...) (nv ...) z e (bn ...) bd ...)
    +     (let ((te z))
    +       (%alet (p ...) (nv ... (e te)) (bn ...) bd ...)))
    +    ((%alet (p ...) (nv ...) ((key z a . e) bn ...) bd ...)
    +     (let ((y z))
    +       (%alet "key" (p ...) (nv ...) y () () (a . e) () (bn ...) bd ...)))
    +    ((%alet "key" (p ...) (nv ...) z ()
    +	    (ndt ...) (((n k) d t ...) . e) (kk ...) (bn ...) bd ...)
    +     (%alet "key" (p ...) (nv ...) z ()
    +	    (ndt ... ((n k) d t ...)) e (kk ... k) (bn ...) bd ...))
    +    ((%alet "key" (p ...) (nv ...) z ()
    +	    (ndt ...) ((n d t ...) . e) (kk ...) (bn ...) bd ...)
    +     (%alet "key" (p ...) (nv ...) z ()
    +	    (ndt ... ((n 'n) d t ...)) e (kk ... 'n) (bn ...) bd ...))
    +    ((%alet "key" (p ...) (nv ...) z ()
    +	    (ndt nd ...) (#t . e) (kk k ...) (bn ...) bd ...)
    +     (%alet "key" (p ...) (nv ...) z (#t)
    +	    (ndt nd ...) e (kk k ...) (bn ...) bd ...))
    +    ((%alet "key" (p ...) (nv ...) z ()
    +	    (ndt nd ...) (#f . e) (kk k ...) (bn ...) bd ...)
    +     (%alet "key" (p ...) (nv ...) z (#f)
    +	    (ndt nd ...) e (kk k ...) (bn ...) bd ...))
    +    ((%alet "key" (p ...) (nv ...) z (o ...)
    +	    (((n k) d t ...) ndt ...) e (kk ...) (bn ...) bd ...)
    +     (let ((x (if (null? z)
    +		  d
    +		  (wow-key! z (o ...) (kk ...) (n k) d t ...))))
    +       (%alet "key" (p ...) (nv ... (n x)) z (o ...)
    +	      (ndt ...) e (kk ...) (bn ...) bd ...)))
    +    ((%alet "key" (p ...) (nv ...) z (o ...) () () (kk ...) (bn ...) bd ...)
    +     (if (null? z)
    +	 (%alet (p ...) (nv ...) (bn ...) bd ...)
    +	 (error "alet: too many arguments" z)))
    +    ((%alet "key" (p ...) (nv ...) z (o ...) () e (kk ...) (bn ...) bd ...)
    +     (let ((te z)) (%alet (p ...) (nv ... (e te)) (bn ...) bd ...)))
    +    ((%alet (p ...) (nv ...) ((rec (n v) (nn vv) ...) bn ...) bd ...)
    +     (%alet "rec" (p ...) (nv ... (n t)) ((n v t))
    +	    ((nn vv) ...) (bn ...) bd ...))
    +    ((%alet "rec" (p ...) (nv ...) (nvt ...) ((n v) (nn vv) ...)
    +	    (bn ...) bd ...)
    +     (%alet "rec" (p ...) (nv ... (n t)) (nvt ... (n v t)) ((nn vv) ...)
    +	    (bn ...) bd ...))
    +    ((%alet "rec" (p ...) (nv ...) ((n v t) ...) () (bn ...) bd ...)
    +     ((let ((n '<undefined>) ...)
    +	(let ((t v) ...)
    +	  (set! n t) ...
    +	  (mu n ...)))
    +      (lambda (t ...) (%alet (p ...) (nv ...) (bn ...) bd ...))))
    +
    +    ((%alet (p ...) (nv ...) ((a b) bn ...) bd ...)
    +     ((lambda (t) (%alet (p ...) (nv ... (a t)) (bn ...) bd ...)) b))
    +
    +    ((%alet (p ...) (nv ...) ((values a c) bn ...) bd ...)
    +     ((lambda (t) (%alet (p ...) (nv ... (a t)) (bn ...) bd ...)) c))
    +    ((%alet (p ...) (nv ...) ((values a b c ...) bn ...) bd ...)
    +     (%alet "not" (p ...) (nv ... (a t)) (values t) (b c ...) (bn ...) bd ...))
    +    ((%alet "not" (p ...) (nv ...) (values t ...) (a b c ...) (bn ...) bd ...)
    +     (%alet "not" (p ...) (nv ... (a tn)) (values t ... tn) (b c ...)
    +	    (bn ...) bd ...))
    +    ((%alet "not" (p ...) (nv ...) (values t ...) (z) (bn ...) bd ...)
    +     (call-with-values (lambda () z)
    +       (lambda (t ...) (%alet (p ...) (nv ...) (bn ...) bd ...))))
    +
    +    ((%alet (p ...) (nv ...) ((a b c ...) bn ...) bd ...)
    +     (%alet "not" (p ...) (nv ... (a t)) (t) (b c ...) (bn ...) bd ...))
    +    ((%alet "not" (p ...) (nv ...) (t ...) (a b c ...) (bn ...) bd ...)
    +     (%alet "not" (p ...) (nv ... (a tn)) (t ... tn) (b c ...) (bn ...)
    +	    bd ...))
    +    ((%alet "not" (p ...) (nv ...) (t ...) (z) (bn ...) bd ...)
    +     (z (lambda (t ...) (%alet (p ...) (nv ...) (bn ...) bd ...))))
    +    ((%alet (p ...) (nv ...) ((a) bn ...) bd ...)
    +     (call-with-current-continuation
    +      (lambda (t) (%alet (p ...) (nv ... (a t)) (bn ...) bd ...))))
    +    ((%alet (p ...) (nv ...) ((a . b) bn ...) bd ...)
    +     (%alet "rot" (p ...) (nv ...) (a) b (bn ...) bd ...))
    +    ((%alet "rot" (p ...) (nv ...) (new-bn ...) (a . b) (bn ...) bd ...)
    +     (%alet "rot" (p ...) (nv ...) (new-bn ... a) b (bn ...) bd ...)) 
    +    ((%alet "rot" (p ...) (nv ...) (()) b (bn ...) bd ...)
    +     (%alet (b (p ...) (nv ...) (bn ...)) () () bd ...))
    +    ((%alet "rot" (p ...) (nv ...) (new-bn ...) b (bn ...) bd ...)
    +     (%alet (b (p ...) (nv ...) (bn ...)) () (new-bn ...) bd ...))
    +    ((%alet (p ...) (nv ...) (a b bn ...) bd ...)
    +     (b (lambda t (%alet (p ...) (nv ... (a t)) (bn ...) bd ...))))))
    +
    +;;; alet*
    +(define-syntax alet*
    +  (syntax-rules (opt cat key rec and values)
    +    ((alet* () bd ...)
    +     ((lambda () bd ...)))
    +    ((alet* ((() a b ...) bn ...) bd ...)
    +     ((lambda () a b ... (alet* (bn ...) bd ...))))
    +    ((alet* (((a) c) bn ...) bd ...)
    +     ((lambda (a) (alet* (bn ...) bd ...)) c))
    +
    +    ((alet* (((values a) c) bn ...)  bd ...)
    +     ((lambda (a) (alet* (bn ...) bd ...)) c))
    +
    +    ((alet* (((values . b) c) bn ...)  bd ...)
    +     (call-with-values (lambda () c)
    +       (lambda* b (alet* (bn ...) bd ...))))
    +    ((alet* (((values . b) c d ...) bn ...) bd ...)
    +     (alet* "dot" (b c d ...) (bn ...) bd ...))
    +    ((alet* "dot" ((a . b) c d ...) (bn ...) bd ...)
    +     ((lambda (a) (alet* "dot" (b d ...) (bn ...) bd ...)) c))
    +    ((alet* "dot" (()) (bn ...) bd ...)
    +     (alet* (bn ...) bd ...))
    +    ((alet* "dot" (b c ...) (bn ...) bd ...)
    +     ((lambda b (alet* (bn ...) bd ...)) c ...))
    +    
    +    ((alet* (((a . b) c) bn ...)  bd ...)
    +     (c (lambda* (a . b) (alet* (bn ...) bd ...))))
    +    ((alet* (((a . b) c d ...) bn ...) bd ...)
    +     ((lambda (a) (alet* "dot" (b d ...) (bn ...) bd ...)) c))
    +
    +    ((alet* ((and (n1 v1 t1 ...) (n2 v2 t2 ...) ...) bn ...) bd ...)
    +     (alet-and* ((n1 v1 t1 ...) (n2 v2 t2 ...) ...) (alet* (bn ...) bd ...)))
    +    ((alet* ((opt z a . e) bn ...) bd ...)
    +     (%alet-opt* z (a . e) (alet* (bn ...) bd ...)))
    +    ((alet* ((cat z a . e) bn ...)  bd ...)
    +     (let ((y z))
    +       (%alet-cat* y (a . e) (alet* (bn ...) bd ...))))
    +    ((alet* ((key z a . e) bn ...)  bd ...)
    +     (let ((y z))
    +       (%alet-key* y () () (a . e) () (alet* (bn ...) bd ...))))
    +    ((alet* ((rec (n1 v1) (n2 v2) ...) bn ...) bd ...)
    +     (alet-rec* ((n1 v1) (n2 v2) ...) (alet* (bn ...) bd ...)))
    +
    +    ((alet* ((a b) bn ...) bd ...)
    +     ((lambda (a) (alet* (bn ...) bd ...)) b))
    +
    +    ((alet* ((values a c) bn ...) bd ...)
    +     ((lambda (a) (alet* (bn ...) bd ...)) c))
    +    ((alet* ((values a b c ...) bn ...) bd ...)
    +     (alet* "not" (values a) (b c ...) (bn ...) bd ...))
    +    ((alet* "not" (values r ...) (a b c ...) (bn ...) bd ...)
    +     (alet* "not" (values r ... a) (b c ...) (bn ...) bd ...))
    +    ((alet* "not" (values r ...) (z) (bn ...) bd ...)
    +     (call-with-values (lambda () z)
    +       (lambda* (r ...) (alet* (bn ...) bd ...))))
    +
    +    ((alet* ((a b c ...) bn ...) bd ...)
    +     (alet* "not" (a) (b c ...) (bn ...) bd ...))
    +    ((alet* "not" (r ...) (a b c ...) (bn ...) bd ...)
    +     (alet* "not" (r ... a) (b c ...) (bn ...) bd ...))
    +    ((alet* "not" (r ...) (z) (bn ...) bd ...)
    +     (z (lambda* (r ...) (alet* (bn ...) bd ...))))
    +    ((alet* ((a) bn ...) bd ...)
    +     (call-with-current-continuation (lambda (a) (alet* (bn ...)  bd ...))))
    +    ((alet* ((a . b) bn ...) bd ...)
    +     (%alet* () () ((a . b) bn ...) bd ...))
    +    ((alet* (a b bn ...) bd ...)
    +     (b (lambda a (alet* (bn ...) bd ...))))
    +    ((alet* var (bn ...) bd ...)
    +     (%alet* (var) () (bn ...) bd ...))))
    +
    +(define-syntax %alet*
    +  (syntax-rules (opt cat key rec and values)
    +    ((%alet* (var) (n ...) () bd ...)
    +     ((letrec ((var (lambda* (n ...) bd ...)))
    +	var) n ...))
    +    ((%alet* (var (bn ...)) (n ...) ()  bd ...)
    +     ((letrec ((var (lambda* (n ...) (alet* (bn ...) bd ...))))
    +	var) n ...))
    +    ((%alet* (var (p ...) (nn ...) (bn ...)) (n ...) ()  bd ...)
    +     ((letrec ((var (lambda* (n ...)
    +			     (%alet* (p ...) (nn ... n ... var) (bn ...)
    +				     bd ...))))
    +	var) n ...))
    +    ((%alet* (p ...) (n ...) ((() a b ...) bn ...) bd ...)
    +     ((lambda () a b ... (%alet* (p ...) (n ...) (bn ...) bd ...))))
    +    ((%alet* (p ...) (n ...) (((a) c) bn ...) bd ...)
    +     ((lambda (a) (%alet* (p ...) (n ... a) (bn ...) bd ...)) c))
    +
    +    ((%alet* (p ...) (n ...) (((values a) c) bn ...) bd ...)
    +     ((lambda (a) (%alet* (p ...) (n ... a) (bn ...) bd ...)) c))
    +
    +    ((%alet* (p ...) (n ...) (((values . b) c) bn ...) bd ...)
    +     (%alet* "one" (p ...) (n ...) (values) (b c) (bn ...) bd ...))
    +    ((%alet* "one" (p ...) (n ...) (values r ...) ((a . b) c) (bn ...) bd ...)
    +     (%alet* "one" (p ...) (n ... a) (values r ... a) (b c) (bn ...) bd ...))
    +    ((%alet* "one" (p ...) (n ...) (values r ...) (() c) (bn ...) bd ...)
    +     (call-with-values (lambda () c)
    +       (lambda* (r ...) (%alet* (p ...) (n ...) (bn ...) bd ...))))
    +    ((%alet* "one" (p ...) (n ...) (values r ...) (b c) (bn ...) bd ...)
    +     (call-with-values (lambda () c)
    +       (lambda* (r ... . b) (%alet* (p ...) (n ... b) (bn ...) bd ...))))
    +
    +    ((%alet* (p ...) (n ...) (((values . b) c d ...) bn ...) bd ...)
    +     (%alet* "dot" (p ...) (n ...) (b c d ...) (bn ...) bd ...))
    +
    +    ((%alet* (p ...) (n ...) (((a . b) c) bn ...) bd ...)
    +     (%alet* "one" (p ...) (n ... a) (a) (b c) (bn ...) bd ...))
    +    ((%alet* "one" (p ...) (n ...) (r ...) ((a . b) c) (bn ...) bd ...)
    +     (%alet* "one" (p ...) (n ... a) (r ... a) (b c) (bn ...) bd ...))
    +    ((%alet* "one" (p ...) (n ...) (r ...) (() c) (bn ...) bd ...)
    +     (c (lambda* (r ...) (%alet* (p ...) (n ...) (bn ...) bd ...))))
    +    ((%alet* "one" (p ...) (n ...) (r ...) (b c) (bn ...) bd ...)
    +     (c (lambda* (r ... . b) (%alet* (p ...) (n ... b) (bn ...) bd ...))))
    +
    +    ((%alet* (p ...) (n ...) (((a . b) c d ...) bn ...) bd ...)
    +     ((lambda (a)
    +	(%alet* "dot" (p ...) (n ... a) (b d ...) (bn ...) bd ...)) c))
    +    ((%alet* "dot" (p ...) (n ...) ((a . b) c d ...) (bn ...) bd ...)
    +     ((lambda (a)
    +	(%alet* "dot" (p ...) (n ... a) (b d ...) (bn ...) bd ...)) c))
    +    ((%alet* "dot" (p ...) (n ...) (()) (bn ...) bd ...)
    +     (%alet* (p ...) (n ...) (bn ...) bd ...))
    +    ((%alet* "dot" (p ...) (n ...) (b c ...) (bn ...) bd ...)
    +     ((lambda b (%alet* (p ...) (n ... b) (bn ...) bd ...)) c ...))
    +
    +    ((%alet* (p ...) (n ...) ((and (n1 v1 t1 ...) (n2 v2 t2 ...) ...) bn ...)
    +	     bd ...)
    +     (alet-and* ((n1 v1 t1 ...) (n2 v2 t2 ...) ...)
    +		(%alet* (p ...) (n ... n1 n2 ...) (bn ...) bd ...)))
    +    ((%alet* (p ...) (n ...) ((opt z a . e) bn ...) bd ...)
    +     (%alet* "opt" (p ...) (n ...) z (a . e) (bn ...) bd ...))
    +    ((%alet* "opt" (p ...) (nn ...) z ((n d t ...)) (bn ...) bd ...)
    +     (let ((n (if (null? z)
    +		  d
    +		  (if (null? (cdr z))
    +		      (wow-opt n (car z) t ...)
    +		      (error "alet*: too many arguments" (cdr z))))))
    +       (%alet* (p ...) (nn ... n) (bn ...) bd ...)))
    +    ((%alet* "opt" (p ...) (nn ...) z ((n d t ...) . e) (bn ...) bd ...)
    +     (let ((y (if (null? z) z (cdr z)))
    +	   (n (if (null? z)
    +		  d
    +		  (wow-opt n (car z) t ...))))
    +       (%alet* "opt" (p ...) (nn ... n) y e (bn ...) bd ...)))
    +    ((%alet* "opt" (p ...) (nn ...) z e (bn ...) bd ...)
    +     (let ((e z))
    +       (%alet* (p ...) (nn ... e) (bn ...) bd ...)))
    +    ((%alet* (p ...) (nn ...) ((cat z a . e) bn ...) bd ...)
    +     (let ((y z))
    +       (%alet* "cat" (p ...) (nn ...) y (a . e) (bn ...) bd ...)))
    +    ((%alet* "cat" (p ...) (nn ...) z ((n d t ...)) (bn ...) bd ...)
    +     (let ((n (if (null? z)
    +		  d
    +		  (if (null? (cdr z))
    +		      (wow-cat-end z n t ...)
    +		      (error "alet*: too many arguments" (cdr z))))))
    +       (%alet* (p ...) (nn ... n) (bn ...) bd ...)))
    +    ((%alet* "cat" (p ...) (nn ...) z ((n d t ...) . e) (bn ...) bd ...)
    +     (let ((n (if (null? z)
    +		  d
    +		  (wow-cat! z n d t ...))))
    +       (%alet* "cat" (p ...) (nn ... n) z e (bn ...) bd ...)))
    +    ((%alet* "cat" (p ...) (nn ...) z e (bn ...) bd ...)
    +     (let ((e z))
    +       (%alet* (p ...) (nn ... e) (bn ...) bd ...)))
    +    ((%alet* (p ...) (m ...) ((key z a . e) bn ...) bd ...)
    +     (let ((y z))
    +       (%alet* "key" (p ...) (m ...) y () () (a . e) () (bn ...) bd ...)))
    +    ((%alet* "key" (p ...) (m ...) z ()
    +	     (ndt ...) (((n k) d t ...) . e) (kk ...) (bn ...) bd ...)
    +     (%alet* "key" (p ...) (m ...) z ()
    +	     (ndt ... ((n k) d t ...)) e (kk ... k) (bn ...) bd ...))
    +    ((%alet* "key" (p ...) (m ...) z ()
    +	     (ndt ...) ((n d t ...) . e) (kk ...) (bn ...) bd ...)
    +     (%alet* "key" (p ...) (m ...) z ()
    +	     (ndt ... ((n 'n) d t ...)) e (kk ... 'n) (bn ...) bd ...))
    +    ((%alet* "key" (p ...) (m ...) z ()
    +	     (ndt nd ...) (#t . e) (kk k ...) (bn ...) bd ...)
    +     (%alet* "key" (p ...) (m ...) z (#t)
    +	     (ndt nd ...) e (kk k ...) (bn ...) bd ...))
    +    ((%alet* "key" (p ...) (m ...) z ()
    +	     (ndt nd ...) (#f . e) (kk k ...) (bn ...) bd ...)
    +     (%alet* "key" (p ...) (m ...) z (#f)
    +	     (ndt nd ...) e (kk k ...) (bn ...) bd ...))
    +    ((%alet* "key" (p ...) (m ...) z (o ...)
    +	     (((n k) d t ...) ndt ...) e (kk ...) (bn ...) bd ...)
    +     (let ((n (if (null? z)
    +		  d
    +		  (wow-key! z (o ...) (kk ...) (n k) d t ...))))
    +       (%alet* "key" (p ...) (m ... n) z (o ...)
    +	       (ndt ...) e (kk ...) (bn ...) bd ...)))
    +    ((%alet* "key" (p ...) (m ...) z (o ...) () () (kk ...) (bn ...) bd ...)
    +     (if (null? z)
    +	 (%alet* (p ...) (m ...) (bn ...) bd ...)
    +	 (error "alet*: too many arguments" z)))
    +    ((%alet* "key" (p ...) (m ...) z (o ...) () e (kk ...) (bn ...) bd ...)
    +     (let ((e z)) (%alet* (p ...) (m ... e) (bn ...) bd ...)))
    +    ((%alet* (p ...) (n ...) ((rec (n1 v1) (n2 v2) ...) bn ...) bd ...)
    +     (alet-rec* ((n1 v1) (n2 v2) ...)
    +		(%alet* (p ...) (n ... n1 n2 ...) (bn ...) bd ...)))
    +
    +    ((%alet* (p ...) (n ...) ((a b) bn ...) bd ...)
    +     ((lambda (a) (%alet* (p ...) (n ... a) (bn ...) bd ...)) b))
    +
    +    ((%alet* (p ...) (n ...) ((values a c) bn ...) bd ...)
    +     ((lambda (a) (%alet* (p ...) (n ... a) (bn ...) bd ...)) c))
    +    ((%alet* (p ...) (n ...) ((values a b c ...) bn ...) bd ...)
    +     (%alet* "not" (p ...) (n ... a) (values a) (b c ...) (bn ...) bd ...))
    +    ((%alet* "not" (p ...) (n ...) (values r ...) (a b c ...) (bn ...) bd ...)
    +     (%alet* "not" (p ...) (n ... a) (values r ... a) (b c ...) (bn ...)
    +	     bd ...))
    +    ((%alet* "not" (p ...) (n ...) (values r ...) (z) (bn ...) bd ...)
    +     (call-with-values (lambda () z)
    +       (lambda* (r ...) (%alet* (p ...) (n ...) (bn ...) bd ...))))
    +
    +    ((%alet* (p ...) (n ...) ((a b c ...) bn ...) bd ...)
    +     (%alet* "not" (p ...) (n ... a) (a) (b c ...) (bn ...) bd ...))
    +    ((%alet* "not" (p ...) (n ...) (r ...) (a b c ...) (bn ...) bd ...)
    +     (%alet* "not" (p ...) (n ... a) (r ... a) (b c ...) (bn ...) bd ...))
    +    ((%alet* "not" (p ...) (n ...) (r ...) (z) (bn ...) bd ...)
    +     (z (lambda* (r ...) (%alet* (p ...) (n ...) (bn ...) bd ...))))
    +    ((%alet* (p ...) (n ...) ((a) bn ...) bd ...)
    +     (call-with-current-continuation
    +      (lambda (a) (%alet* (p ...) (n ... a) (bn ...) bd ...))))
    +    ((%alet* (p ...) (n ...) ((a . b) bn ...) bd ...)
    +     (%alet* "rot" (p ...) (n ...) (a) b (bn ...) bd ...))
    +    ((%alet* "rot" (p ...) (n ...) (new-bn ...) (a . b) (bn ...) bd ...)
    +     (%alet* "rot" (p ...) (n ...) (new-bn ... a) b (bn ...) bd ...)) 
    +    ((%alet* "rot" () () (()) b (bn ...) bd ...)
    +     (%alet* (b (bn ...)) () () bd ...))
    +    ((%alet* "rot" (p ...) (n ...) (()) b (bn ...) bd ...)
    +     (%alet* (b (p ...) (n ...) (bn ...)) () () bd ...))
    +    ((%alet* "rot" () () (new-bn ...) b (bn ...) bd ...)
    +     (%alet* (b (bn ...)) () (new-bn ...) bd ...))
    +    ((%alet* "rot" (p ...) (n ...) (new-bn ...) b (bn ...) bd ...)
    +     (%alet* (b (p ...) (n ...) (bn ...)) () (new-bn ...) bd ...))
    +    ((%alet* (p ...) (n ...) (a b bn ...) bd ...)
    +     (b (lambda a (%alet* (p ...) (n ... a) (bn ...) bd ...))))))
    +
    +;;; auxiliaries
    +(define-syntax lambda*
    +  (syntax-rules ()
    +    ((lambda* (a . e) bd ...)
    +     (lambda* "star" (ta) (a) e bd ...))
    +    ((lambda* "star" (t ...) (n ...) (a . e) bd ...)
    +     (lambda* "star" (t ... ta) (n ... a) e bd ...))
    +    ((lambda* "star" (t ...) (n ...) () bd ...)
    +     (lambda (t ...)
    +       (let* ((n t) ...) bd ...)))
    +    ((lambda* "star" (t ...) (n ...) e bd ...)
    +     (lambda (t ... . te)
    +       (let* ((n t) ... (e te)) bd ...)))
    +    ((lambda* e bd ...)
    +     (lambda e bd ...))))
    +
    +(define-syntax alet-and
    +  (syntax-rules ()
    +    ((alet-and ((n v t ...) ...) bd ...)
    +     (alet-and "and" () ((n v t ...) ...) bd ...))
    +    ((alet-and "and" (nt ...) ((n v) nvt ...) bd ...)
    +     (let ((t v))
    +       (and t (alet-and "and" (nt ... (n t)) (nvt ...) bd ...))))
    +    ((alet-and "and" (nt ...) ((n v t) nvt ...) bd ...)
    +     (let ((tt v))
    +       (and (let ((n tt)) t)
    +	    (alet-and "and" (nt ... (n tt)) (nvt ...) bd ...))))
    +    ((alet-and "and" ((n t) ...) () bd ...)
    +     ((lambda (n ...) bd ...) t ...))))
    +
    +(define-syntax alet-and*
    +  (syntax-rules ()
    +    ((alet-and* () bd ...)
    +     ((lambda () bd ...)))
    +    ((alet-and* ((n v) nvt ...) bd ...)
    +     (let ((n v))
    +       (and n (alet-and* (nvt ...) bd ...))))
    +    ((alet-and* ((n v t) nvt ...) bd ...)
    +     (let ((n v))
    +       (and t (alet-and* (nvt ...) bd ...))))))
    +
    +(define-syntax alet-rec
    +  (syntax-rules ()
    +    ((alet-rec ((n v) ...) bd ...)
    +     (alet-rec "rec" () ((n v) ...) bd ...))
    +    ((alet-rec "rec" (nvt ...) ((n v) nv ...) bd ...)
    +     (alet-rec "rec" (nvt ... (n v t)) (nv ...) bd ...))
    +    ((alet-rec "rec" ((n v t) ...) () bd ...)
    +     (let ((n '<undefined>) ...)
    +       (let ((t v) ...)
    +	 (set! n t) ...
    +	 ;;(let ()
    +	 ;;  bd ...))))))
    +	 bd ...)))))
    +
    +(define-syntax alet-rec*
    +  (syntax-rules ()
    +    ((alet-rec* ((n v) ...) bd ...)
    +     (let* ((n '<undefined>) ...)
    +       (set! n v) ...
    +       ;;(let ()
    +       ;; bd ...)))))
    +       bd ...))))
    +
    +(define-syntax wow-opt
    +  (syntax-rules ()
    +    ((wow-opt n v)
    +     v)
    +    ((wow-opt n v t)
    +     (let ((n v))
    +       (if t n (error "alet[*]: bad argument" n 'n 't))))
    +    ((wow-opt n v t ts)
    +     (let ((n v))
    +       (if t ts (error "alet[*]: bad argument" n 'n 't))))
    +    ((wow-opt n v t ts fs)
    +     (let ((n v))
    +       (if t ts fs)))))
    +
    +(define-syntax wow-opt!
    +  (syntax-rules ()
    +    ((wow-opt! z n)
    +     (let ((n (car z)))
    +       (set! z (cdr z))
    +       n))
    +    ((wow-opt! z n t)
    +     (let ((n (car z)))
    +       (if t
    +	   (begin (set! z (cdr z)) n)
    +	   (error "alet[*]: bad argument" n 'n 't))))
    +    ((wow-opt! z n t ts)
    +     (let ((n (car z)))
    +       (if t
    +	   (begin (set! z (cdr z)) ts)
    +	   (error "alet[*]: bad argument" n 'n 't))))
    +    ((wow-opt! z n t ts fs)
    +     (let ((n (car z)))
    +       (if t
    +	   (begin (set! z (cdr z)) ts)
    +	   (begin (set! z (cdr z)) fs))))))
    +
    +(define-syntax wow-cat-end
    +  (syntax-rules ()
    +    ((wow-cat-end z n)
    +     (car z))
    +    ((wow-cat-end z n t)
    +     (let ((n (car z)))
    +       (if t n (error "alet[*]: too many argument" z))))
    +    ((wow-cat-end z n t ts)
    +     (let ((n (car z)))
    +       (if t ts (error "alet[*]: too many argument" z))))
    +    ((wow-cat-end z n t ts fs)
    +     (let ((n (car z)))
    +       (if t ts fs)))))
    +
    +(define-syntax wow-cat
    +  (syntax-rules ()
    +    ((wow-cat z n d)
    +     z)
    +    ((wow-cat z n d t)
    +     (let ((n (car z)))
    +       (if t
    +	   z
    +	   (let lp ((head (list n)) (tail (cdr z)))
    +	     (if (null? tail)
    +		 (cons d z)
    +		 (let ((n (car tail)))
    +		   (if t
    +		       (cons n (append (reverse head) (cdr tail)))
    +		       (lp (cons n head) (cdr tail)))))))))
    +    ((wow-cat z n d t ts)
    +     (let ((n (car z)))
    +       (if t
    +	   (cons ts (cdr z))
    +	   (let lp ((head (list n)) (tail (cdr z)))
    +	     (if (null? tail)
    +		 (cons d z)
    +		 (let ((n (car tail)))
    +		   (if t
    +		       (cons ts (append (reverse head) (cdr tail)))
    +		       (lp (cons n head) (cdr tail)))))))))
    +    ((wow-cat z n d t ts fs)
    +     (let ((n (car z)))
    +       (if t
    +	   (cons ts (cdr z))
    +	   (cons fs (cdr z)))))))
    +
    +(define-syntax wow-cat!
    +  (syntax-rules ()
    +    ((wow-cat! z n d)
    +     (let ((n (car z)))
    +       (set! z (cdr z))
    +       n))
    +    ((wow-cat! z n d t)
    +     (let ((n (car z)))
    +       (if t
    +	   (begin (set! z (cdr z)) n)
    +	   (let lp ((head (list n)) (tail (cdr z)))
    +	     (if (null? tail)
    +		 d
    +		 (let ((n (car tail)))
    +		   (if t
    +		       (begin (set! z (append (reverse head) (cdr tail))) n)
    +		       (lp (cons n head) (cdr tail)))))))))
    +    ((wow-cat! z n d t ts)
    +     (let ((n (car z)))
    +       (if t
    +	   (begin (set! z (cdr z)) ts)
    +	   (let lp ((head (list n)) (tail (cdr z)))
    +	     (if (null? tail)
    +		 d
    +		 (let ((n (car tail)))
    +		   (if t
    +		       (begin (set! z (append (reverse head) (cdr tail))) ts)
    +		       (lp (cons n head) (cdr tail)))))))))
    +    ((wow-cat! z n d t ts fs)
    +     (let ((n (car z)))
    +       (if t
    +	   (begin (set! z (cdr z)) ts)
    +	   (begin (set! z (cdr z)) fs))))))
    +
    +(define-syntax wow-key!
    +  (syntax-rules ()
    +    ((wow-key! z () (kk ...) (n key) d)
    +     (let ((x (car z))
    +	   (y (cdr z)))
    +       (if (null? y)
    +	   d
    +	   (if (equal? key x)
    +	       (begin (set! z (cdr y)) (car y))
    +	       (let lp ((head (list (car y) x)) (tail (cdr y)))
    +		 (if (null? tail)
    +		     d
    +		     (let ((x (car tail))
    +			   (y (cdr tail)))
    +		       (if (null? y)
    +			   d
    +			   (if (equal? key x)
    +			       (begin (set! z (append (reverse head) (cdr y)))
    +				      (car y))
    +			       (lp (cons (car y) (cons x head))
    +				   (cdr y)))))))))))
    +    ((wow-key! z (#f) (kk ...) (n key) d)
    +     (let ((x (car z))
    +	   (y (cdr z)))
    +       (if (null? y)
    +	   d
    +	   (if (equal? key x)
    +	       (begin (set! z (cdr y)) (car y))
    +	       (let ((lk (list kk ...)))
    +		 (if (not (member x lk))
    +		     d
    +		     (let lp ((head (list (car y) x)) (tail (cdr y)))
    +		       (if (null? tail)
    +			   d
    +			   (let ((x (car tail))
    +				 (y (cdr tail)))
    +			     (if (null? y)
    +				 d
    +				 (if (equal? key x)
    +				     (begin (set! z (append (reverse head)
    +							    (cdr y)))
    +					    (car y))
    +				     (if (not (member x lk))
    +					 d
    +					 (lp (cons (car y) (cons x head))
    +					     (cdr y))))))))))))))
    +    ((wow-key! z (#t) (kk ...) (n key) d)
    +     (let ((x (car z))
    +	   (y (cdr z)))
    +       (if (null? y)
    +	   d
    +	   (if (equal? key x)
    +	       (begin (set! z (cdr y)) (car y))
    +	       (let* ((lk (list kk ...))
    +		      (m (member x lk)))
    +		 (let lp ((head (if m (list (car y) x) (list x)))
    +			  (tail (if m (cdr y) y)))
    +		   (if (null? tail)
    +		       d
    +		       (let ((x (car tail))
    +			     (y (cdr tail)))
    +			 (if (null? y)
    +			     d
    +			     (if (equal? key x)
    +				 (begin (set! z (append (reverse head)
    +							(cdr y)))
    +					(car y))
    +				 (let ((m (member x lk)))
    +				   (lp (if m
    +					   (cons (car y) (cons x head))
    +					   (cons x head))
    +				       (if m (cdr y) y)))))))))))))
    +    ((wow-key! z () (kk ...) (n key) d t)
    +     (let ((x (car z))
    +	   (y (cdr z)))
    +       (if (null? y)
    +	   d
    +	   (if (equal? key x)
    +	       (let ((n (car y)))
    +		 (if t
    +		     (begin (set! z (cdr y)) n)
    +		     (error "alet[*]: bad argument" n 'n 't)))
    +	       (let lp ((head (list (car y) x)) (tail (cdr y)))
    +		 (if (null? tail)
    +		     d
    +		     (let ((x (car tail))
    +			   (y (cdr tail)))
    +		       (if (null? y)
    +			   d
    +			   (if (equal? key x)
    +			       (let ((n (car y)))
    +				 (if t
    +				     (begin (set! z (append (reverse head)
    +							    (cdr y)))
    +					    n)
    +				     (error "alet[*]: bad argument"
    +					    n 'n 't)))
    +			       (lp (cons (car y) (cons x head))
    +				   (cdr y)))))))))))
    +    ((wow-key! z (#f) (kk ...) (n key) d t)
    +     (let ((x (car z))
    +	   (y (cdr z)))
    +       (if (null? y)
    +	   d
    +	   (if (equal? key x)
    +	       (let ((n (car y)))
    +		 (if t
    +		     (begin (set! z (cdr y)) n)
    +		     (error "alet[*]: bad argument" n 'n 't)))
    +	       (let ((lk (list kk ...)))
    +		 (if (not (member x lk))
    +		     d
    +		     (let lp ((head (list (car y) x)) (tail (cdr y)))
    +		       (if (null? tail)
    +			   d
    +			   (let ((x (car tail))
    +				 (y (cdr tail)))
    +			     (if (null? y)
    +				 d
    +				 (if (equal? key x)
    +				     (let ((n (car y)))
    +				       (if t
    +					   (begin
    +					     (set! z (append (reverse head)
    +							     (cdr y)))
    +					     n)
    +					   (error "alet[*]: bad argument"
    +						  n 'n 't)))
    +				     (if (not (member x lk))
    +					 d
    +					 (lp (cons (car y) (cons x head))
    +					     (cdr y))))))))))))))
    +    ((wow-key! z (#t) (kk ...) (n key) d t)
    +     (let ((x (car z))
    +	   (y (cdr z)))
    +       (if (null? y)
    +	   d
    +	   (if (equal? key x)
    +	       (let ((n (car y)))
    +		 (if t
    +		     (begin (set! z (cdr y)) n)
    +		     (error "alet[*]: bad argument" n 'n 't)))
    +	       (let* ((lk (list kk ...))
    +		      (m (member x lk)))
    +		 (let lp ((head (if m (list (car y) x) (list x)))
    +			  (tail (if m (cdr y) y)))
    +		   (if (null? tail)
    +		       d
    +		       (let ((x (car tail))
    +			     (y (cdr tail)))
    +			 (if (null? y)
    +			     d
    +			     (if (equal? key x)
    +				 (let ((n (car y)))
    +				   (if t
    +				       (begin (set! z (append (reverse head)
    +							      (cdr y)))
    +					      n)
    +				       (error "alet[*]: bad argument"
    +					      n 'n 't)))
    +				 (let ((m (member x lk)))
    +				   (lp (if m
    +					   (cons (car y) (cons x head))
    +					   (cons x head))
    +				       (if m (cdr y) y)))))))))))))
    +    ((wow-key! z () (kk ...) (n key) d t ts)
    +     (let ((x (car z))
    +	   (y (cdr z)))
    +       (if (null? y)
    +	   d
    +	   (if (equal? key x)
    +	       (let ((n (car y)))
    +		 (if t
    +		     (begin (set! z (cdr y)) ts)
    +		     (error "alet[*]: bad argument" n 'n 't)))
    +	       (let lp ((head (list (car y) x)) (tail (cdr y)))
    +		 (if (null? tail)
    +		     d
    +		     (let ((x (car tail))
    +			   (y (cdr tail)))
    +		       (if (null? y)
    +			   d
    +			   (if (equal? key x)
    +			       (let ((n (car y)))
    +				 (if t
    +				     (begin (set! z (append (reverse head)
    +							    (cdr y)))
    +					    ts)
    +				     (error "alet[*]: bad argument"
    +					    n 'n 't)))
    +			       (lp (cons (car y) (cons x head))
    +				   (cdr y)))))))))))
    +    ((wow-key! z (#f) (kk ...) (n key) d t ts)
    +     (let ((x (car z))
    +	   (y (cdr z)))
    +       (if (null? y)
    +	   d
    +	   (if (equal? key x)
    +	       (let ((n (car y)))
    +		 (if t
    +		     (begin (set! z (cdr y)) ts)
    +		     (error "alet[*]: bad argument" n 'n 't)))
    +	       (let ((lk (list kk ...)))
    +		 (if (not (member x lk))
    +		     d
    +		     (let lp ((head (list (car y) x)) (tail (cdr y)))
    +		       (if (null? tail)
    +			   d
    +			   (let ((x (car tail))
    +				 (y (cdr tail)))
    +			     (if (null? y)
    +				 d
    +				 (if (equal? key x)
    +				     (let ((n (car y)))
    +				       (if t
    +					   (begin
    +					     (set! z (append (reverse head)
    +							     (cdr y)))
    +					     ts)
    +					   (error "alet[*]: bad argument"
    +						  n 'n 't)))
    +				     (if (not (member x lk))
    +					 d
    +					 (lp (cons (car y) (cons x head))
    +					     (cdr y))))))))))))))
    +    ((wow-key! z (#t) (kk ...) (n key) d t ts)
    +     (let ((x (car z))
    +	   (y (cdr z)))
    +       (if (null? y)
    +	   d
    +	   (if (equal? key x)
    +	       (let ((n (car y)))
    +		 (if t
    +		     (begin (set! z (cdr y)) ts)
    +		     (error "alet[*]: bad argument" n 'n 't)))
    +	       (let* ((lk (list kk ...))
    +		      (m (member x lk)))
    +		 (let lp ((head (if m (list (car y) x) (list x)))
    +			  (tail (if m (cdr y) y)))
    +		   (if (null? tail)
    +		       d
    +		       (let ((x (car tail))
    +			     (y (cdr tail)))
    +			 (if (null? y)
    +			     d
    +			     (if (equal? key x)
    +				 (let ((n (car y)))
    +				   (if t
    +				       (begin (set! z (append (reverse head)
    +							      (cdr y)))
    +					      ts)
    +				       (error "alet[*]: bad argument"
    +					      n 'n 't)))
    +				 (let ((m (member x lk)))
    +				   (lp (if m
    +					   (cons (car y) (cons x head))
    +					   (cons x head))
    +				       (if m (cdr y) y)))))))))))))
    +    ((wow-key! z () (kk ...) (n key) d t ts fs)
    +     (let ((x (car z))
    +	   (y (cdr z)))
    +       (if (null? y)
    +	   d
    +	   (if (equal? key x)
    +	       (let ((n (car y)))
    +		 (if t
    +		     (begin (set! z (cdr y)) ts)
    +		     (begin (set! z (cdr y)) fs)))
    +	       (let lp ((head (list (car y) x)) (tail (cdr y)))
    +		 (if (null? tail)
    +		     d
    +		     (let ((x (car tail))
    +			   (y (cdr tail)))
    +		       (if (null? y)
    +			   d
    +			   (if (equal? key x)
    +			       (let ((n (car y)))
    +				 (if t
    +				     (begin (set! z (append (reverse head)
    +							    (cdr y)))
    +					    ts)
    +				     (begin (set! z (append (reverse head)
    +							    (cdr y)))
    +					    fs)))
    +			       (lp (cons (car y) (cons x head))
    +				   (cdr y)))))))))))
    +    ((wow-key! z (#f) (kk ...) (n key) d t ts fs)
    +     (let ((x (car z))
    +	   (y (cdr z)))
    +       (if (null? y)
    +	   d
    +	   (if (equal? key x)
    +	       (let ((n (car y)))
    +		 (if t
    +		     (begin (set! z (cdr y)) ts)
    +		     (begin (set! z (cdr y)) fs)))
    +	       (let ((lk (list kk ...)))
    +		 (if (not (member x lk))
    +		     d
    +		     (let lp ((head (list (car y) x)) (tail (cdr y)))
    +		       (if (null? tail)
    +			   d
    +			   (let ((x (car tail))
    +				 (y (cdr tail)))
    +			     (if (null? y)
    +				 d
    +				 (if (equal? key x)
    +				     (let ((n (car y)))
    +				       (if t
    +					   (begin
    +					     (set! z (append (reverse head)
    +							     (cdr y)))
    +					     ts)
    +					   (begin
    +					     (set! z (append (reverse head)
    +							     (cdr y)))
    +					     fs)))
    +				     (if (not (member x lk))
    +					 d
    +					 (lp (cons (car y) (cons x head))
    +					     (cdr y))))))))))))))
    +    ((wow-key! z (#t) (kk ...) (n key) d t ts fs)
    +     (let ((x (car z))
    +	   (y (cdr z)))
    +       (if (null? y)
    +	   d
    +	   (if (equal? key x)
    +	       (let ((n (car y)))
    +		 (if t
    +		     (begin (set! z (cdr y)) ts)
    +		     (begin (set! z (cdr y)) fs)))
    +	       (let* ((lk (list kk ...))
    +		      (m (member x lk)))
    +		 (let lp ((head (if m (list (car y) x) (list x)))
    +			  (tail (if m (cdr y) y)))
    +		   (if (null? tail)
    +		       d
    +		       (let ((x (car tail))
    +			     (y (cdr tail)))
    +			 (if (null? y)
    +			     d
    +			     (if (equal? key x)
    +				 (let ((n (car y)))
    +				   (if t
    +				       (begin (set! z (append (reverse head)
    +							      (cdr y)))
    +					      ts)
    +				       (begin (set! z (append (reverse head)
    +							      (cdr y)))
    +					      fs)))
    +				 (let ((m (member x lk)))
    +				   (lp (if m
    +					   (cons (car y) (cons x head))
    +					   (cons x head))
    +				       (if m (cdr y) y)))))))))))))))
    +
    +(define-syntax alet-opt*
    +  (syntax-rules ()
    +    ((alet-opt* z (a . e) bd ...)
    +     (let ((y z))
    +       (%alet-opt* y (a . e) bd ...)))))
    +(define-syntax %alet-opt*
    +  (syntax-rules ()
    +    ((%alet-opt* z ((n d t ...)) bd ...)
    +     (let ((n (if (null? z)
    +		  d
    +		  (if (null? (cdr z))
    +		      (wow-opt n (car z) t ...)
    +		      (error "alet*: too many arguments" (cdr z))))))
    +       bd ...))
    +    ((%alet-opt* z ((n d t ...) . e)  bd ...)
    +     (let ((y (if (null? z) z (cdr z)))
    +	   (n (if (null? z)
    +		  d
    +		  (wow-opt n (car z) t ...))))
    +       (%alet-opt* y e bd ...)))
    +    ((%alet-opt* z e bd ...)
    +     (let ((e z)) bd ...))))
    +;; (define-syntax %alet-opt*
    +;;   (syntax-rules ()
    +;;     ((%alet-opt* z ((n d t ...)) bd ...)
    +;;      (let ((n (if (null? z)
    +;; 		  d
    +;; 		  (if (null? (cdr z))
    +;; 		      (wow-opt n (car z) t ...)
    +;; 		      (error "alet*: too many arguments" (cdr z))))))
    +;;        bd ...))
    +;;     ((%alet-opt* z ((n d t ...) . e)  bd ...)
    +;;      (let ((n (if (null? z)
    +;; 		  d
    +;; 		  (wow-opt! z n t ...))))
    +;;        (%alet-opt* z e bd ...)))
    +;;     ((%alet-opt* z e bd ...)
    +;;      (let ((e z)) bd ...))))
    +;; (define-syntax %alet-opt*
    +;;   (syntax-rules ()
    +;;     ((%alet-opt* z (ndt ...) (a . e) bd ...)
    +;;      (%alet-opt* z (ndt ... a) e bd ...))
    +;;     ((%alet-opt* z ((n d t ...) (nn dd tt ...) ...) () bd ...)
    +;;      (if (null? z)
    +;; 	 (let* ((n d) (nn dd) ...) bd ...)
    +;; 	 (let ((y (cdr z))
    +;; 	       (n (wow-opt n (car z) t ...)))
    +;; 	   (%alet-opt* y ((nn dd tt ...) ...) () bd ...))))
    +;;     ((%alet-opt* z () () bd ...)
    +;;      (if (null? z)
    +;; 	 (let () bd ...)
    +;; 	 (error "alet*: too many arguments" z)))
    +;;     ((%alet-opt* z  ((n d t ...) (nn dd tt ...) ...) e bd ...)
    +;;      (if (null? z)
    +;; 	 (let* ((n d) (nn dd) ... (e z)) bd ...)
    +;; 	 (let ((y (cdr z))
    +;; 	       (n (wow-opt n (car z) t ...)))
    +;; 	   (%alet-opt* y ((nn dd tt ...) ...) e bd ...))))
    +;;     ((%alet-opt* z () e bd ...)
    +;;      (let ((e z)) bd ...))))
    +
    +(define-syntax alet-cat*
    +  (syntax-rules ()
    +    ((alet-cat* z (a . e) bd ...)
    +     (let ((y z))
    +       (%alet-cat* y (a . e) bd ...)))))
    +;; (define-syntax %alet-cat*
    +;;   (syntax-rules ()
    +;;     ((%alet-cat* z ((n d t ...)) bd ...)
    +;;      (let ((n (if (null? z)
    +;; 		  d
    +;; 		  (if (null? (cdr z))
    +;; 		      (wow-cat-end z n t ...)
    +;; 		      (error "alet*: too many arguments" (cdr z))))))
    +;;        bd ...))
    +;;     ((%alet-cat* z ((n d t ...) . e) bd ...)
    +;;      (let* ((w (if (null? z)
    +;; 		   (cons d z)
    +;; 		   (wow-cat z n d t ...)))
    +;; 	    (n (car w))
    +;; 	    (y (cdr w)))
    +;;        (%alet-cat* y e bd ...)))
    +;;     ((%alet-cat* z e bd ...)
    +;;      (let ((e z)) bd ...))))
    +(define-syntax %alet-cat*
    +  (syntax-rules ()
    +    ((%alet-cat* z ((n d t ...)) bd ...)
    +     (let ((n (if (null? z)
    +		  d
    +		  (if (null? (cdr z))
    +		      (wow-cat-end z n t ...)
    +		      (error "alet*: too many arguments" (cdr z))))))
    +       bd ...))
    +    ((%alet-cat* z ((n d t ...) . e) bd ...)
    +     (let ((n (if (null? z)
    +		  d
    +		  (wow-cat! z n d t ...))))
    +       (%alet-cat* z e bd ...)))
    +    ((%alet-cat* z e bd ...)
    +     (let ((e z)) bd ...))))
    +;; (define-syntax %alet-cat*
    +;;   (syntax-rules ()
    +;;     ((%alet-cat* z (ndt ...) (a . e) bd ...)
    +;;      (%alet-cat* z (ndt ... a) e bd ...))
    +;;     ((%alet-cat* z ((n d t ...) (nn dd tt ...) ...) () bd ...)
    +;;      (if (null? z)
    +;; 	 (let* ((n d) (nn dd) ...) bd ...)
    +;; 	 (let* ((w (wow-cat z n d t ...))
    +;; 		(n (car w))
    +;; 		(y (cdr w)))
    +;; 	   (%alet-cat* y ((nn dd tt ...) ...) () bd ...))))
    +;;     ((%alet-cat* z () () bd ...)
    +;;      (if (null? z)
    +;; 	 (let () bd ...)
    +;; 	 (error "alet*: too many arguments" z)))
    +;;     ((%alet-cat* z  ((n d t ...) (nn dd tt ...) ...) e bd ...)
    +;;      (if (null? z)
    +;; 	 (let* ((n d) (nn dd) ... (e z)) bd ...)
    +;; 	 (let* ((w (wow-cat z n d t ...))
    +;; 		(n (car w))
    +;; 		(y (cdr w)))
    +;; 	   (%alet-cat* y ((nn dd tt ...) ...) e bd ...))))
    +;;     ((%alet-cat* z () e bd ...)
    +;;      (let ((e z)) bd ...))))
    +
    +(define-syntax alet-key*
    +  (syntax-rules ()
    +    ((alet-key* z (a . e) bd ...)
    +     (let ((y z))
    +       (%alet-key* y () () (a . e) () bd ...)))))
    +(define-syntax %alet-key*
    +  (syntax-rules ()
    +    ((%alet-key* z () (ndt ...) (((n k) d t ...) . e) (kk ...) bd ...)
    +     (%alet-key* z () (ndt ... ((n k) d t ...)) e (kk ... k) bd ...))
    +    ((%alet-key* z () (ndt ...) ((n d t ...) . e) (kk ...) bd ...)
    +     (%alet-key* z () (ndt ... ((n 'n) d t ...)) e (kk ... 'n) bd ...))
    +    ((%alet-key* z () (ndt nd ...) (#f . e) (kk k ...) bd ...)
    +     (%alet-key* z (#f) (ndt nd ...) e (kk k ...) bd ...))
    +    ((%alet-key* z () (ndt nd ...) (#t . e) (kk k ...) bd ...)
    +     (%alet-key* z (#t) (ndt nd ...) e (kk k ...) bd ...))
    +
    +    ((%alet-key* z (o ...) (((n k) d t ...) ndt ...) e (kk ...) bd ...)
    +     (let ((n (if (null? z)
    +		  d
    +		  (wow-key! z (o ...) (kk ...) (n k) d t ...))))
    +       (%alet-key* z (o ...) (ndt ...) e (kk ...) bd ...)))
    +    ((%alet-key* z (o ...) () () (kk ...) bd ...)
    +     (if (null? z)
    +	 (let () bd ...)
    +	 (error "alet*: too many arguments" z)))
    +    ((%alet-key* z (o ...) () e (kk ...) bd ...)
    +     (let ((e z)) bd ...))))
    +
    + +

    References

    + +
      +
    • [R5RS] Richard Kelsey, William Clinger, and Jonathan Rees: Revised(5) + Report on the Algorithmic Language Scheme + Link
    • +
    • [SRFI 2] Oleg Kiselyov: and-let*: and and with local bindings, a guarded + let* special form. + Link
    • +
    • [SRFI 11] Lars T. Hansen: Syntax for receiving multiple values. + Link
    • +
    • [SRFI 51] Joo ChurlSoo: Handling rest list. + Link
    • +
    • [SRFI 71] Sebastian Egner: Extended let-syntax for multiple values. + Link
    • +
    • [Scsh] Olin Shivers, Brian Carlstrom, Martin Gasbichler, Mike Sperber + Link
    • +
    + +

    Copyright

    + +Copyright (c) 2006 Joo ChurlSoo. +

    +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: +

    +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. +

    +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +


    +
    Editor: Mike + Sperber
    + + + diff --git a/doc/srfi-std/srfi-87.html b/doc/srfi-std/srfi-87.html new file mode 100644 index 0000000000..73986bb6bf --- /dev/null +++ b/doc/srfi-std/srfi-87.html @@ -0,0 +1,141 @@ + +SRFI 87: => in case clauses + +

    Title

    + +=> in case clauses + +

    Author

    + +Chongkai Zhu + +

    Status

    + +This SRFI is currently in ``final'' status. To see an explanation of each +status that a SRFI can hold, see +here. +To provide input on this SRFI, please +mailto:srfi-87@srfi.schemers.org. +See instructions +here to subscribe to the list. You can access the discussion via +the +archive of the mailing list. +You can access +post-finalization messages via + +the archive of the mailing list. +

    +

    + +

    Abstract

    This SRFI proposes an extension to the case syntax +to allow the => clauses as in cond. +

    Rationale

    case is introduced as a syntax sugar based on +cond, which helps to save a explicit calling to let. +But without the => clause, if the result expression +needs the value of key, the let can't be saved. For an +easy example, suppose we want the following: +
    (case (get-symbol)
    +  ((true) #t)
    +  ((false) #f)
    +  (else => (lambda (x) x)))
    +

    Without the => clause in case, we have to +write:

    +
    (let ((key (get-symbol)))
    +  (cond ((eq? key 'true) #t)
    +        ((eq? key 'false) #f)
    +        (else key)))
    +

    Specification

    +

    (Based on R5RS section 4.2.1 Conditionals)

    +
    +
    library syntax: case <key> <clause1> + <clause2> ... +
    Syntax: <Key> may be any expression. Each <clause> + should have the form
    ((<datum1> ...) <expression1> <expression2> ...),
    +

    where each <datum> is an external representation of some object. All + the <datum>s must be distinct. The last <clause> may be an "else + clause," which has the form

    (else <expression1> <expression2> ...).
    +

    Alternatively, a <clause> may be of the form +

    ((<datum1> ...) => <expression>)
    +

    and the last <clause> may be of the + form

    (else => <expression>)
    +

    Semantics: A `case' expression is evaluated as + follows. <Key> is evaluated and its result is compared against each + <datum>. If the result of evaluating <key> is equivalent (in the + sense of `eqv?'; see section see section 6.1 Equivalence + predicates) to a <datum>, then the expressions in the corresponding + <clause> are evaluated from left to right and the result(s) of the last + expression in the <clause> is(are) returned as the result(s) of the + `case' expression. If the result of evaluating <key> is + different from every <datum>, then if there is an else clause its + expressions are evaluated and the result(s) of the last is(are) the result(s) + of the `case' expression; otherwise the result of the + `case' expression is unspecified. If the + selected <clause> uses the => alternate form, then the + <expression> is evaluated. Its value must be a procedure that accepts + one argument; this procedure is then called on the value of <Key> and + the value(s) returned by this procedure is(are) returned by the + `case' expression.

    +

    (Based on R5RS section 3.5 Proper tail recursion)

    +
    If a cond or case expression is in a tail context, and has a clause of the +form (<expression1> => <expression2>) then the +(implied) call to the procedure that results from the evaluation of <expression2> +is in a tail context. <expression2> itself is not in a tail context.
    +

    Implementation

    +
    (define-syntax case
    +  (syntax-rules (else =>)
    +    ((case (key ...)
    +       clauses ...)
    +     (let ((atom-key (key ...)))
    +       (case atom-key clauses ...)))
    +    ((case key
    +       (else => result))
    +     (result key))
    +    ((case key
    +       ((atoms ...) => result))
    +     (if (memv key '(atoms ...))
    +         (result key)))
    +    ((case key
    +       ((atoms ...) => result)
    +       clause clauses ...)
    +     (if (memv key '(atoms ...))
    +         (result key)
    +         (case key clause clauses ...)))
    +    ((case key
    +       (else result1 result2 ...))
    +     (begin result1 result2 ...))
    +    ((case key
    +       ((atoms ...) result1 result2 ...))
    +     (if (memv key '(atoms ...))
    +         (begin result1 result2 ...)))
    +    ((case key
    +       ((atoms ...) result1 result2 ...)
    +       clause clauses ...)
    +     (if (memv key '(atoms ...))
    +         (begin result1 result2 ...)
    +         (case key clause clauses ...)))))
    +

    Copyright

    Copyright (C) 2006 Chongkai Zhu. All Rights Reserved. +

    Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: +

    The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. +

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +


    + +
    Editor: Mike +Sperber
    Last +modified: Mon Apr 10 21:20:25 CEST 2006 + diff --git a/doc/srfi-std/srfi-9.html b/doc/srfi-std/srfi-9.html new file mode 100644 index 0000000000..1f334ae795 --- /dev/null +++ b/doc/srfi-std/srfi-9.html @@ -0,0 +1,403 @@ + + + + SRFI 9: Defining Record Types + + + + +

    Title

    + +SRFI 9: Defining Record Types + +

    Author

    + +Richard Kelsey + +

    Status

    + +This SRFI is currently in ``final'' status. To see an explanation of each status that a SRFI can hold, see here. +You can access the discussion via the archive of the mailing list. +

      +
    • Received: 1999/07/01 +
    • Revised: 1999/08/25 +
    • Draft: 1999/07/07-1999/09/06 +
    • Final: 1999/09/09 +
    + +

    Abstract

    + +This SRFI describes syntax for creating new data types, called record types. +A predicate, constructor, and field accessors and modifiers are defined for +each record type. Each new record type is distinct from all existing types, +including other record types and Scheme's predefined types. + +

    Rationale

    + +Many Scheme implementations provide means for creating new types, + usually called either records or structures. +The DEFINE-RECORD-TYPE syntax described here is a slight + simplification of one written for Scheme 48 by Jonathan Rees. +Unlike many record-defining macros or special forms, it + does not create any new identifiers. +Instead, the names of the + record type, predicate, constructor, and so on are all listed explicitly + in the source. +This has the following advantages: +
      +
    • It can be defined using a simple SYNTAX-RULES macro + in Scheme implementations that provide a procedural interface + for creating record types. +
    • It does not restrict users to a particular naming convention. +
    • Tools like grep and GNU Emacs's tag facility will see the + defining occurance of each identifier. +
    + +

    Specification

    + +The syntax of a record-type definition is: + +
    + <command or definition>           
    +   -> <record type definition>           ; addition to 8.1.6 in R5RS
    +
    + <record type definition>
    +   -> (define-record-type <type name>
    +	(<constructor name> <field tag> ...)
    +	<predicate name>
    +	<field spec> ...)
    +
    + <field spec> -> (<field tag> <accessor name>)
    +              -> (<field tag> <accessor name> <modifier name>)
    +
    + <field tag> -> <identifier>
    + <... name>  -> <identifier>
    +
    + +DEFINE-RECORD-TYPE is generative: +each use creates a new record type that is distinct from all existing types, +including other record types and Scheme's predefined types. + +Record-type definitions may only occur at top-level (there are two +possible semantics for `internal' record-type definitions, generative +and nongenerative, and no consensus as to which is better). + +

    +An instance of DEFINE-RECORD-TYPE is equivalent to the following +definitions: +

      +
    • <type name> is bound to a representation of the record + type itself. Operations on record types, such as defining print + methods, reflection, etc. are left to other SRFIs. + +
    • <constructor name> is bound to a procedure that takes + as many arguments as there are <field tag>s in the + (<constructor name> ...) + subform and returns a new <type name> record. + Fields whose tags are listed with <constructor name> + have the corresponding argument as their + initial value. The initial values of all other fields are unspecified. + +
    • <predicate name> is a predicate that returns #T when + given a value returned by <constructor name> and #F + for everything else. + +
    • Each <accessor name> is a procedure that takes a record + of type <type name> and returns the current value of the + corresponding field. + It is an error to pass an accessor a value which is not a record of + the appropriate type. + +
    • Each <modifier name> is a procedure that takes a record + of type <type name> and a value which becomes the new + value of the corresponding field; an unspecified value is returned. + It is an error to pass a modifier a first + argument which is not a record of the appropriate type. +
    + +

    +Records are disjoint from the types listed in Section 4.2 of R5RS. + +

    +Set!ing the value of any of these identifiers has no + effect on the behavior of any of their original values. + +

    +The following +

    +  (define-record-type :pare
    +    (kons x y)
    +    pare?
    +    (x kar set-kar!)
    +    (y kdr))
    +
    +defines KONS to be a constructor, KAR and +KDR to be accessors, SET-KAR! to be a modifier, +and PARE? to be a predicate for :PAREs. + +
    +  (pare? (kons 1 2))        --> #t
    +  (pare? (cons 1 2))        --> #f
    +  (kar (kons 1 2))          --> 1
    +  (kdr (kons 1 2))          --> 2
    +  (let ((k (kons 1 2)))
    +    (set-kar! k 3)
    +    (kar k))                --> 3
    +
    + +

    Implementation

    + +This code is divided into three layers. In top-down order these are: +
      +
    1. Syntax definitions for DEFINE-RECORD-TYPE and an auxillary + macro. +
    2. An implementation of record types with a procedural interface. +Some Scheme implementations already have something close to this. +
    3. Vector-like records implemented in R5RS. This redefines some standard +Scheme procedures and therefor must be loaded before any other code, including +part 2 above. Note that these procedures can be used to break the +record-type abstraction (for example, RECORD-SET! can be used +to modify the type of a record). Access to these procedures should be +restricted. +
    + +

    Syntax definitions

    + +
    +; Definition of DEFINE-RECORD-TYPE
    +
    +(define-syntax define-record-type
    +  (syntax-rules ()
    +    ((define-record-type type
    +       (constructor constructor-tag ...)
    +       predicate
    +       (field-tag accessor . more) ...)
    +     (begin
    +       (define type
    +         (make-record-type 'type '(field-tag ...)))
    +       (define constructor
    +         (record-constructor type '(constructor-tag ...)))
    +       (define predicate
    +         (record-predicate type))
    +       (define-record-field type field-tag accessor . more)
    +       ...))))
    +
    +; An auxilliary macro for define field accessors and modifiers.
    +; This is needed only because modifiers are optional.
    +
    +(define-syntax define-record-field
    +  (syntax-rules ()
    +    ((define-record-field type field-tag accessor)
    +     (define accessor (record-accessor type 'field-tag)))
    +    ((define-record-field type field-tag accessor modifier)
    +     (begin
    +       (define accessor (record-accessor type 'field-tag))
    +       (define modifier (record-modifier type 'field-tag))))))
    +
    + +

    Record types

    + +
    +; We define the following procedures:
    +; 
    +; (make-record-type <type-name <field-names>)    -> <record-type>
    +; (record-constructor <record-type<field-names>) -> <constructor>
    +; (record-predicate <record-type>)               -> <predicate>
    +; (record-accessor <record-type <field-name>)    -> <accessor>
    +; (record-modifier <record-type <field-name>)    -> <modifier>
    +;   where
    +; (<constructor> <initial-value> ...)         -> <record>
    +; (<predicate> <value>)                       -> <boolean>
    +; (<accessor> <record>)                       -> <value>
    +; (<modifier> <record> <value>)         -> <unspecific>
    +
    +; Record types are implemented using vector-like records.  The first
    +; slot of each record contains the record's type, which is itself a
    +; record.
    +
    +(define (record-type record)
    +  (record-ref record 0))
    +
    +;----------------
    +; Record types are themselves records, so we first define the type for
    +; them.  Except for problems with circularities, this could be defined as:
    +;  (define-record-type :record-type
    +;    (make-record-type name field-tags)
    +;    record-type?
    +;    (name record-type-name)
    +;    (field-tags record-type-field-tags))
    +; As it is, we need to define everything by hand.
    +
    +(define :record-type (make-record 3))
    +(record-set! :record-type 0 :record-type)	; Its type is itself.
    +(record-set! :record-type 1 ':record-type)
    +(record-set! :record-type 2 '(name field-tags))
    +
    +; Now that :record-type exists we can define a procedure for making more
    +; record types.
    +
    +(define (make-record-type name field-tags)
    +  (let ((new (make-record 3)))
    +    (record-set! new 0 :record-type)
    +    (record-set! new 1 name)
    +    (record-set! new 2 field-tags)
    +    new))
    +
    +; Accessors for record types.
    +
    +(define (record-type-name record-type)
    +  (record-ref record-type 1))
    +
    +(define (record-type-field-tags record-type)
    +  (record-ref record-type 2))
    +
    +;----------------
    +; A utility for getting the offset of a field within a record.
    +
    +(define (field-index type tag)
    +  (let loop ((i 1) (tags (record-type-field-tags type)))
    +    (cond ((null? tags)
    +           (error "record type has no such field" type tag))
    +          ((eq? tag (car tags))
    +           i)
    +          (else
    +           (loop (+ i 1) (cdr tags))))))
    +
    +;----------------
    +; Now we are ready to define RECORD-CONSTRUCTOR and the rest of the
    +; procedures used by the macro expansion of DEFINE-RECORD-TYPE.
    +
    +(define (record-constructor type tags)
    +  (let ((size (length (record-type-field-tags type)))
    +        (arg-count (length tags))
    +        (indexes (map (lambda (tag)
    +                        (field-index type tag))
    +                      tags)))
    +    (lambda args
    +      (if (= (length args)
    +             arg-count)
    +          (let ((new (make-record (+ size 1))))
    +            (record-set! new 0 type)
    +            (for-each (lambda (arg i)
    +			(record-set! new i arg))
    +                      args
    +                      indexes)
    +            new)
    +          (error "wrong number of arguments to constructor" type args)))))
    +
    +(define (record-predicate type)
    +  (lambda (thing)
    +    (and (record? thing)
    +         (eq? (record-type thing)
    +              type))))
    +
    +(define (record-accessor type tag)
    +  (let ((index (field-index type tag)))
    +    (lambda (thing)
    +      (if (and (record? thing)
    +               (eq? (record-type thing)
    +                    type))
    +          (record-ref thing index)
    +          (error "accessor applied to bad value" type tag thing)))))
    +
    +(define (record-modifier type tag)
    +  (let ((index (field-index type tag)))
    +    (lambda (thing value)
    +      (if (and (record? thing)
    +               (eq? (record-type thing)
    +                    type))
    +          (record-set! thing index value)
    +          (error "modifier applied to bad value" type tag thing)))))
    +
    + +

    Records

    + +
    +; This implements a record abstraction that is identical to vectors,
    +; except that they are not vectors (VECTOR? returns false when given a
    +; record and RECORD? returns false when given a vector).  The following
    +; procedures are provided:
    +;   (record? <value>)                -> <boolean>
    +;   (make-record <size>)             -> <record>
    +;   (record-ref <record> <index>)    -> <value>
    +;   (record-set! <record> <index> <value>) -> <unspecific>
    +;
    +; These can implemented in R5RS Scheme as vectors with a distinguishing
    +; value at index zero, providing VECTOR? is redefined to be a procedure
    +; that returns false if its argument contains the distinguishing record
    +; value.  EVAL is also redefined to use the new value of VECTOR?.
    +
    +; Define the marker and redefine VECTOR? and EVAL.
    +
    +(define record-marker (list 'record-marker))
    +
    +(define real-vector? vector?)
    +
    +(define (vector? x)
    +  (and (real-vector? x)
    +       (or (= 0 (vector-length x))
    +	   (not (eq? (vector-ref x 0)
    +		record-marker)))))
    +
    +; This won't work if ENV is the interaction environment and someone has
    +; redefined LAMBDA there.
    +
    +(define eval
    +  (let ((real-eval eval))
    +    (lambda (exp env)
    +      ((real-eval `(lambda (vector?) ,exp))
    +       vector?))))
    +
    +; Definitions of the record procedures.
    +
    +(define (record? x)
    +  (and (real-vector? x)
    +       (< 0 (vector-length x))
    +       (eq? (vector-ref x 0)
    +            record-marker)))
    +
    +(define (make-record size)
    +  (let ((new (make-vector (+ size 1))))
    +    (vector-set! new 0 record-marker)
    +    new))
    +
    +(define (record-ref record index)
    +  (vector-ref record (+ index 1)))
    +
    +(define (record-set! record index value)
    +  (vector-set! record (+ index 1) value))
    +
    + +

    Copyright

    + +

    Copyright (C) Richard Kelsey (1999). All Rights Reserved.

    + +

    +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: +

    +

    +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. +

    +

    +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +

    + +
    + +
    Editor: Mike Sperber
    + + +