From 05607ad6615dfd0bbbb9593d5b620898d373b3b3 Mon Sep 17 00:00:00 2001 From: Matthew Flatt Date: Tue, 12 Jun 2007 01:44:02 +0000 Subject: [PATCH] work on docs for keyword arguments svn: r6581 --- collects/mzlib/etc.ss | 30 ++++ collects/scribble/basic.ss | 2 +- collects/scribble/manual.ss | 47 +++--- collects/scribble/scheme.ss | 20 ++- collects/scribble/scribble.css | 5 + collects/scribblings/guide/apply.scrbl | 54 +++++-- collects/scribblings/guide/data.scrbl | 1 + collects/scribblings/guide/define.scrbl | 6 +- collects/scribblings/guide/forms.scrbl | 8 +- collects/scribblings/guide/keywords.scrbl | 22 ++- collects/scribblings/guide/lambda.scrbl | 69 +++++--- collects/scribblings/guide/paths.scrbl | 2 +- collects/scribblings/guide/regexps-data.scrbl | 8 + collects/scribblings/reference/data.scrbl | 3 +- .../scribblings/reference/procedures.scrbl | 149 ++++++++++++++++++ collects/scribblings/reference/syntax.scrbl | 111 +++++++------ 16 files changed, 401 insertions(+), 136 deletions(-) create mode 100644 collects/scribblings/guide/regexps-data.scrbl create mode 100644 collects/scribblings/reference/procedures.scrbl diff --git a/collects/mzlib/etc.ss b/collects/mzlib/etc.ss index 3fd0ec0c48..f4121c45d3 100644 --- a/collects/mzlib/etc.ss +++ b/collects/mzlib/etc.ss @@ -25,6 +25,8 @@ opt-lambda define-opt + keyword-apply + make-keyword-procedure local recur @@ -192,6 +194,34 @@ (define id (opt-lambda args body1 body ...))] [(_ . rest) (define . rest)])) + (define (keyword-apply f kw-args normal-args . normal-argss) + (apply f (append (apply append + (map (lambda (p) (list (car p) (cdr p))) kw-args)) + (if (null? normal-argss) + normal-args + (cons normal-args + (let loop ([normal-argss normal-argss]) + (if (null? (cdr normal-argss)) + (car normal-argss) + (cons (car normal-argss) + (loop (cdr normal-argss)))))))))) + + (define (make-keyword-procedure f) + (lambda args + (let loop ([args args] + [normal null] + [kw null]) + (cond + [(null? args) (apply f kw (reverse normal))] + [(and (keyword? (car args)) + (pair? (cdr args))) + (loop (cddr args) + normal + (cons (cons (car args) (cadr args)) kw))] + [else (loop (cdr args) + (cons (car args) normal) + kw)])))) + (define (sort-kws len need-kw l) (for-each (lambda (kw) (unless (memq kw l) diff --git a/collects/scribble/basic.ss b/collects/scribble/basic.ss index b326fa63b4..071635f0f5 100644 --- a/collects/scribble/basic.ss +++ b/collects/scribble/basic.ss @@ -96,7 +96,7 @@ (make-element 'subscript (decode-content str))) (define/kw (superscript #:body str) - (make-element superscript (decode-content str))) + (make-element 'superscript (decode-content str))) ;; ---------------------------------------- diff --git a/collects/scribble/manual.ss b/collects/scribble/manual.ss index 96670c923d..28ffad59eb 100644 --- a/collects/scribble/manual.ss +++ b/collects/scribble/manual.ss @@ -92,7 +92,7 @@ (provide onscreen menuitem defterm schemefont schemevalfont schemeresultfont schemeidfont - schemeparenfont schemekeywordfont + schemeparenfont schemekeywordfont schememetafont file exec link procedure idefterm) @@ -116,6 +116,8 @@ (make-element "schemesymbol" (decode-content str))) (define/kw (schemeparenfont #:body str) (make-element "schemeparen" (decode-content str))) + (define/kw (schememetafont #:body str) + (make-element "schememeta" (decode-content str))) (define/kw (schemekeywordfont #:body str) (make-element "schemekeyword" (decode-content str))) (define/kw (file #:body str) @@ -161,9 +163,9 @@ (schemeresultfont "#")) (define dots0 - (make-element "schemeparen" (list "..."))) + (make-element "schememeta" (list "..."))) (define dots1 - (make-element "schemeparen" (list "..." (superscript "+")))) + (make-element "schememeta" (list "...+"))) (define-syntax (arg-contract stx) (syntax-case stx (... ...+) @@ -322,7 +324,7 @@ (if (keyword? (car i)) (cadr i) (car i)))) - (apply append (map cdr prototypes)))]) + (apply append (map cdr prototypes)))]) (make-splice (cons (make-table @@ -437,21 +439,22 @@ (make-paragraph (list (to-element - `(struct ,(make-target-element* - (to-element name) - (let ([name (if (pair? name) - (car name) - name)]) - (list* (list name) - (list name '?) - (list 'make- name) - (append - (map (lambda (f) - (list name '- (car f))) - fields) - (map (lambda (f) - (list 'set- name '- (car f) '!)) - fields))))) + `(,(schemeparenfont "struct") + ,(make-target-element* + (to-element name) + (let ([name (if (pair? name) + (car name) + name)]) + (list* (list name) + (list name '?) + (list 'make- name) + (append + (map (lambda (f) + (list name '- (car f))) + fields) + (map (lambda (f) + (list 'set- name '- (car f) '!)) + fields))))) ,(map car fields)))))))) (map (lambda (v) (cond @@ -506,7 +509,8 @@ [(pair? form) (append (loop (car form)) (loop (cdr form)))] [else null]))) - forms))]) + forms))] + [current-meta-list '(... ...+)]) (make-splice (cons (make-table @@ -550,7 +554,8 @@ [(pair? form) (append (loop (car form)) (loop (cdr form)))] [else null])) - (current-variable-list))]) + (current-variable-list))] + [current-meta-list '(... ...+)]) (make-blockquote "leftindent" (cons diff --git a/collects/scribble/scheme.ss b/collects/scribble/scheme.ss index ba19bfc4ac..8c6eccc309 100644 --- a/collects/scribble/scheme.ss +++ b/collects/scribble/scheme.ss @@ -15,14 +15,16 @@ syntax-ize-hook current-keyword-list current-variable-list + current-meta-list (struct shaped-parens (val shape))) (define no-color "schemeplain") - (define meta-color "schemeplain") + (define reader-color "schemeplain") (define keyword-color "schemekeyword") (define comment-color "schemecomment") (define paren-color "schemeparen") + (define meta-color "schememeta") (define value-color "schemevalue") (define symbol-color "schemesymbol") (define variable-color "schemevariable") @@ -39,6 +41,8 @@ set!))) (define current-variable-list (make-parameter null)) + (define current-meta-list + (make-parameter null)) (define defined-names (make-hash-table)) @@ -246,7 +250,7 @@ [(unsyntax) (values "#," 0)])]) (out str (if (positive? (+ quote-depth quote-delta)) value-color - meta-color)) + reader-color)) (let ([i (cadr (syntax->list c))]) (set! src-col (or (syntax-column i) src-col)) (hash-table-put! next-col-map src-col dest-col) @@ -339,8 +343,6 @@ (char=? (string-ref s 0) #\_)) (values (substring s 1) #t #f) (values s #f #f))))] - [(is-kw?) (and (identifier? c) - (memq (syntax-e c) (current-keyword-list)))] [(is-var?) (and (identifier? c) (memq (syntax-e c) (current-variable-list)))]) (if (element? (syntax-e c)) @@ -375,8 +377,12 @@ value-color] [(identifier? c) (cond - [is-kw? + [(and (identifier? c) + (memq (syntax-e c) (current-keyword-list))) keyword-color] + [(and (identifier? c) + (memq (syntax-e c) (current-meta-list))) + meta-color] [is-var? variable-color] [it? variable-color] @@ -526,7 +532,9 @@ (list #f 1 col (+ 1 col) (+ 2 vec-sz - (sub1 (length l)) + (if (zero? (length l)) + 0 + (sub1 (length l))) (apply + (map syntax-span l)))))))] [(pair? v) (let* ([a (syntax-ize (car v) (+ col 1))] diff --git a/collects/scribble/scribble.css b/collects/scribble/scribble.css index 48ffaf7dcf..a13c34d0b7 100644 --- a/collects/scribble/scribble.css +++ b/collects/scribble/scribble.css @@ -165,6 +165,11 @@ font-family: Courier; font-size: 80%; } + .schememeta { + color: #262680; + font-family: Courier; font-size: 80%; + } + .schemeopt { color: black; } diff --git a/collects/scribblings/guide/apply.scrbl b/collects/scribblings/guide/apply.scrbl index 9ae0cf335f..cbec70e0bd 100644 --- a/collects/scribblings/guide/apply.scrbl +++ b/collects/scribblings/guide/apply.scrbl @@ -37,13 +37,15 @@ procedure's @idefterm{arity} is the number(s) of arguments that it accepts. @;------------------------------------------------------------------------ -@section{Keyword Arguments} +@section[#:tag "guide:keyword-args"]{Keyword Arguments} Some procedures accept @defterm{keyword arguments} in addition to by-position arguments. For that case, an @scheme[_arg] can be an @scheme[_arg-keyword _arg-expr] sequence instead of just a @scheme[_arg-expr]: +@margin-note{For an introduction to keywords, see @secref["guide:keywords"].} + @specform/subs[ (_proc-expr _arg ...) ([arg arg-expr @@ -55,23 +57,24 @@ For example, @schemeblock[(go "super.ss" #:mode 'fast)] calls the procedure bound to @scheme[go] with @scheme["super.ss"] as a -by-position argument, and with @scheme['fast] as an argument associated -with the @scheme[#:mode] keyword. Thus, a keyword is implicitly paired -with the expression that follows it. Since a keyword by itself is not -an expression, then +by-position argument, and with @scheme['fast] as an argument +associated with the @scheme[#:mode] keyword. A keyword is implicitly +paired with the expression that follows it. + +Since a keyword by itself is not an expression, then @schemeblock[(go "super.ss" #:mode #:fast)] -is a syntax error---because @scheme[#:fast] is not an expression. +is a syntax error. The @scheme[#:mode] keyword must be followed by an +expression to produce an argument value, and @scheme[#:fast] is not an +expression. The order of keyword @scheme[_arg]s determines the order in which -@scheme[_arg-expr]s are evaluated, but a procedure accepts (or -declines) keyword arguments independent of their position in the -argument list. That is, keyword arguments are recognized by an applied -procedure using only the associated keyword. The above call to -@scheme[go] can be equivalently written +@scheme[_arg-expr]s are evaluated, but a procedure accepts keyword +arguments independent of their position in the argument list. The +above call to @scheme[go] can be equivalently written -@schemeblock[(go #:mode #:fast "super.ss")] +@schemeblock[(go #:mode 'fast "super.ss")] @refdetails["mz:application"]{procedure applications} @@ -98,8 +101,8 @@ of the items in the list: (avg '(1 2)) ] -The @scheme[apply] procedure offers a way around this restriction, It -takes another procedure a @italic{list} arguments, and it applies the +The @scheme[apply] procedure offers a way around this restriction. It +takes a procedure and a @italic{list} arguments, and it applies the procedure to the arguments: @def+int[ @@ -109,3 +112,26 @@ procedure to the arguments: (avg '(1 2)) (avg '(1 2 3 4)) ] + +As a convenience, the @scheme[apply] produce accepts additional +arguments between the procedure and the list. The additional arguments +are effectively @scheme[cons]ed onto the argument list: + +@def+int[ +(define (anti-sum lst) + (apply - 0 lst)) +(anti-sum '(1 2 3)) +] + +The @scheme[apply] procedure supports only by-position arguments. To +apply a procedure with keyword arguments, use the +@scheme[keyword-apply] procedure, which accepts a procedure to apply +and two lists. The first list contains contains pairs, each matching a +keyword with its corresponding value. The second list contains +by-position procedure arguments, as for @scheme[apply]. + +@schemeblock[ +(keyword-apply go + (list (cons '#:mode 'fast)) + (list "super.ss")) +] diff --git a/collects/scribblings/guide/data.scrbl b/collects/scribblings/guide/data.scrbl index 060eb7f3e7..1f2f7eb3fe 100644 --- a/collects/scribblings/guide/data.scrbl +++ b/collects/scribblings/guide/data.scrbl @@ -23,5 +23,6 @@ built-in datatypes for simple forms of data. @include-section["boxes.scrbl"] @include-section["hash-tables.scrbl"] @include-section["paths.scrbl"] +@include-section["regexps-data.scrbl"] @include-section["keywords.scrbl"] @include-section["void-and-undef.scrbl"] diff --git a/collects/scribblings/guide/define.scrbl b/collects/scribblings/guide/define.scrbl index f7d3952d8e..5b5fe6eff6 100644 --- a/collects/scribblings/guide/define.scrbl +++ b/collects/scribblings/guide/define.scrbl @@ -5,7 +5,7 @@ @interaction-eval[(require (rename (lib "etc.ss") define define-opt))] -@title{Definitions: @scheme[define] and @scheme[define-values]} +@title{Definitions: @scheme[define]} A basic definition has the form @@ -65,7 +65,7 @@ which is a shorthand ] @;------------------------------------------------------------------------ -@section[#:tag "guide:multiple-values"]{Multiple Values} +@section[#:tag "guide:multiple-values"]{Multiple Values and @scheme[define-values]} A Scheme expression normally produces a single result, but some expressions can produce multiple results. For example, @@ -78,7 +78,7 @@ but @scheme[quotient/remainder] produces the same two values at once: (quotient/remainder 13 3) ] -As shown abpve, the REPL prints each result value on its own line. +As shown above, the REPL prints each result value on its own line. Multiple-valued procedures can be implemented in terms of the @scheme[values] procedure, which takes any number of values and diff --git a/collects/scribblings/guide/forms.scrbl b/collects/scribblings/guide/forms.scrbl index 5308c82729..c4c44cb90e 100644 --- a/collects/scribblings/guide/forms.scrbl +++ b/collects/scribblings/guide/forms.scrbl @@ -51,10 +51,10 @@ forms, where square brackets are normally used (by convention). That is square brackets @italic{do not} mean optional parts of the syntactic form. -A @scheme[...] indicates zero or more repetitions of the preceding -form, and @scheme[...+] indicates one or more repetitions of the -preceding datum. Otherwise, non-italicized identifiers stand form -themselves. +A @schememetafont{...} indicates zero or more repetitions of the +preceding form, and @schememetafont{...+} indicates one or more +repetitions of the preceding datum. Otherwise, non-italicized +identifiers stand form themselves. Based on the above grammar, then, here are a few legal uses of @schemekeywordfont{something}: diff --git a/collects/scribblings/guide/keywords.scrbl b/collects/scribblings/guide/keywords.scrbl index 0f7f1849a2..960a2ff07c 100644 --- a/collects/scribblings/guide/keywords.scrbl +++ b/collects/scribblings/guide/keywords.scrbl @@ -3,10 +3,11 @@ @require[(lib "eval.ss" "scribble")] @require["guide-utils.ss"] -@title[#:tag "keywords"]{Keywords} +@title[#:tag "guide:keywords"]{Keywords} -A @defterm{keyword} is similar to a symbol (see @secref["guide:symbols"]), -but its printed form is prefixed with @litchar{#:}. +A @defterm{keyword} value is similar to a symbol (see +@secref["guide:symbols"]), but its printed form is prefixed with +@litchar{#:}. @refdetails/gory["mz:parse-keyword"]{the syntax of keywords} @@ -16,17 +17,22 @@ but its printed form is prefixed with @litchar{#:}. (eq? '#:apple (string->keyword "apple")) ] -Although keywords are values, an unquoted keyword is not an -expression, just as an unquoted identifier does not produce a symbol: +More precisely, a keyword is analogous to an identifier; in the same +way that an identifier can be quoted to produce a symbol, a keyword +can be quoted to produce a value. The same term ``keyword'' is used in +both cases, but we sometimes use @defterm{keyword value} to refer more +specifically to the result of a quote-keyword expression or of +@scheme[string->keyword]. An unquoted keyword is not an expression, +just as an unquoted identifier does not produce a symbol: @examples[ not-a-symbol-expression #:not-a-keyword-expression ] -Despite their similarities, keywords are used differently than -symbols. Keywords are intented for use (unquoted) as special markers -in argument lists and in certain syntactic forms. +Despite their similarities, keywords are used in a different way than +identifiers or symbols. Keywords are intented for use (unquoted) as +special markers in argument lists and in certain syntactic forms. @italic{Need some examples here, once we have more keyword-based procedures and syntax in place...} diff --git a/collects/scribblings/guide/lambda.scrbl b/collects/scribblings/guide/lambda.scrbl index afe2dba2c7..e39f16695f 100644 --- a/collects/scribblings/guide/lambda.scrbl +++ b/collects/scribblings/guide/lambda.scrbl @@ -4,10 +4,12 @@ @require["guide-utils.ss"] @interaction-eval[(require (rename (lib "etc.ss") lambda opt-lambda))] +@interaction-eval[(require (only (lib "etc.ss") keyword-apply + make-keyword-procedure))] -@title[#:tag "guide:lambda"]{Procedures: @scheme[lambda] and @scheme[case-lambda]} +@title[#:tag "guide:lambda"]{Procedures: @scheme[lambda]} -A @scheme[lambda] expression creates a procedure. In the simplest +Such a @scheme[lambda] expression creates a procedure. In the simplest case, a @scheme[lambda] expression has the form @specform[ @@ -28,7 +30,7 @@ A @scheme[lambda] form with @math{n} @scheme[_arg-id]s accepts ] @;------------------------------------------------------------------------ -@section{Rest Arguments} +@section{Declaring a Rest Argument} A @scheme[lambda] expression can also have the form @@ -50,8 +52,8 @@ into a list bound to @scheme[_rest-id]. 1 2 3) ] -Procedures of this form often use @scheme[apply] to call another -procedure that accepts any number of arguments. +Procedures with a @scheme[_rest-id] often use @scheme[apply] to call +another procedure that accepts any number of arguments. @margin-note{See @secref["guide:apply"] for more information on @scheme[apply].} @@ -65,17 +67,17 @@ procedure that accepts any number of arguments. (max-mag 1 -2 0) ] -Generalizing the fixed-arity and any-arity style, a @scheme[lambda] -expression can have the form +The @scheme[lambda] form also supports required arguments combined +with a @scheme[_rest-id]: @specform[ (lambda (arg-id ...+ . rest-id) body ...+) ] -The result is a procedure that requires at least as many arguments as -@scheme[_arg-id]s, and also accepts any number of additional -arguments. +The result of this form is a procedure that requires at least as many +arguments as @scheme[_arg-id]s, and also accepts any number of +additional arguments. @defexamples[ (define max-mag @@ -90,16 +92,19 @@ A @scheme[_rest-id] variable is sometimes called a @defterm{rest argument}, because it accepts the ``rest'' of the procedure arguments. @;------------------------------------------------------------------------ -@section{Optional Arguments} +@section{Declaring Optional Arguments} Instead of just an identifier, an argument (other than a rest argument) in a @scheme[lambda] form can be specified with an identifier and a default value: @specform/subs[ -(lambda (arg ...+ . rest-id) +(lambda gen-formals body ...+) -([arg arg-id +([gen-formals (arg ...) + rest-id + (arg ...+ . rest-id)] + [arg arg-id [arg-id default-expr]]) ]{} @@ -129,15 +134,18 @@ and every following @scheme[_arg-id] must have a default as well. (greet "Adam") ] -@section{Keyword Arguments} +@section[#:tag "guide:lambda-keywords"]{Declaring Keyword Arguments} A @scheme[lambda] form can declare an argument to be passed by keyword, instead of position. Keyword arguments can be mixed with by-position arguments, and default-value expressions can be supplied for either kind of argument: +@margin-note{For an introduction to applications with keywords, + see @secref["guide:keyword-args"].} + @specform/subs[ -(lambda (arg ...+ . rest-id) +(lambda gen-formals body ...+) ([arg arg-id [arg-id default-expr] @@ -145,7 +153,7 @@ for either kind of argument: (code:line arg-keyword [arg-id default-expr])]) ]{} -As argument specified as @scheme[(code:line _arg-keyword _arg-id)] is +An argument specified as @scheme[(code:line _arg-keyword _arg-id)] is supplied by an application using the same @scheme[_arg-keyword]. The position of the keyword--identifier pair in the argument list does not matter for matching with arguments in an application, because it will @@ -160,7 +168,7 @@ be matched to an argument value by keyword instead of by position. (greet #:last "Doe" "John") ] -A @scheme[(code:line _arg-keyword [_arg-id _default-expr])] argument +An @scheme[(code:line _arg-keyword [_arg-id _default-expr])] argument specifies a keyword-based argument with a default value. @defexamples[ @@ -174,6 +182,29 @@ specifies a keyword-based argument with a default value. (greet "Karl" #:last "Marx" #:hi "Guten Tag") ] +The @scheme[lambda] form does not directly support the creation +of a procedure that accepts ``rest'' keywords. To construct a +procedure that accepts any and all keyword arguments, use +@scheme[make-keyword-procedure]. The procedure supplied to +@scheme[make-keyword-procedure] receives all keyword arguments as +a list of pairs through the first (by-position) argument, and +then all by-position arguments from an application as the +remaining by-position arguments. + +@margin-note{For an introduction to @scheme[keyword-apply], see +@secref["guide:apply"].} + +@defexamples[ +(define (trace-wrap f) + (make-keyword-procedure + (lambda (kw-args . rest) + (printf "Called with ~s ~s\n" kw-args rest) + (keyword-apply f kw-args rest)))) +((trace-wrap greet) "John" #:hi "Howdy") +] + +@refdetails["mz:lambda"]{procedure expressions} + @;------------------------------------------------------------------------ @section{Arity-Sensitive Procedures: @scheme[case-lambda]} @@ -190,7 +221,7 @@ that are supplied. A case-lambda expression has the form (arg-id ...+ . rest-id)]) ] -where each @scheme[_formals _body ...+] is anlogous to @scheme[(lambda +where each @scheme[[_formals _body ...+]] is analogous to @scheme[(lambda _formals _body ...+)]. Applying a procedure produced by @scheme[case-lambda] is like applying a @scheme[lambda] for the first case that matches the number of given arguments. @@ -208,5 +239,3 @@ case that matches the number of given arguments. A @scheme[case-lambda] procedure cannot directly support optional or keyword arguments. - -@refdetails["mz:lambda"]{procedure expressions} diff --git a/collects/scribblings/guide/paths.scrbl b/collects/scribblings/guide/paths.scrbl index ebd91f8609..f15c470875 100644 --- a/collects/scribblings/guide/paths.scrbl +++ b/collects/scribblings/guide/paths.scrbl @@ -3,7 +3,7 @@ @require[(lib "eval.ss" "scribble")] @require["guide-utils.ss"] -@title[#:tag "paths"]{Paths} +@title[#:tag "guide:paths"]{Paths} A @defterm{path} encapsulates a filesystem path that (potentially) names a file or directory. Although paths can be converted to and from diff --git a/collects/scribblings/guide/regexps-data.scrbl b/collects/scribblings/guide/regexps-data.scrbl new file mode 100644 index 0000000000..c0e14dc3df --- /dev/null +++ b/collects/scribblings/guide/regexps-data.scrbl @@ -0,0 +1,8 @@ +#reader(lib "docreader.ss" "scribble") +@require[(lib "manual.ss" "scribble")] +@require[(lib "eval.ss" "scribble")] +@require["guide-utils.ss"] + +@title[#:tag "guide:regexp-literals"]{Regexps} + +A @defterm{regexp} is a pattern for regular-expression matching. diff --git a/collects/scribblings/reference/data.scrbl b/collects/scribblings/reference/data.scrbl index b878ee2786..de63135f5b 100644 --- a/collects/scribblings/reference/data.scrbl +++ b/collects/scribblings/reference/data.scrbl @@ -202,7 +202,8 @@ If the @scheme[lst]s are empty, then @scheme[#f] is returned.} @; ---------------------------------------------------------------------- @include-section["sequences.scrbl"] -@section[#:tag "mz:procedures"]{Procedures} +@; ---------------------------------------------------------------------- +@include-section["procedures.scrbl"] @; ---------------------------------------------------------------------- @section[#:tag "void"]{Void and Undefined} diff --git a/collects/scribblings/reference/procedures.scrbl b/collects/scribblings/reference/procedures.scrbl new file mode 100644 index 0000000000..1e21f9a0e7 --- /dev/null +++ b/collects/scribblings/reference/procedures.scrbl @@ -0,0 +1,149 @@ +#reader(lib "docreader.ss" "scribble") +@require["mz.ss"] + +@interaction-eval[(require (rename (lib "etc.ss") lambda opt-lambda))] +@interaction-eval[(require (only (lib "etc.ss") keyword-apply + make-keyword-procedure))] + +@title[#:tag "mz:procedures"]{Procedures} + +@defproc[(procedure? [v any/c]) boolean]{ Returns @scheme[#t] if +@scheme[v] is a procedure, @scheme[#f] otherwise.} + + +@defproc[(apply [proc procedure?] [v any/c] ... [lst list?]) any]{ + +@guideintro["guide:apply"]{@scheme[apply]} + +Applies @scheme[proc] using the content of @scheme[(list* v ... lst)] +as the (by-position) arguments. The given @scheme[proc] must accept as +many arguments as the number of @scheme[v]s plus length of +@scheme[lst], and it must not require any keyword arguments; +otherwise, the @exnraise[exn:fail:contract]. The given @scheme[proc] +is called in tail position with respect to the @scheme[apply] call. + +@examples[ +(apply + '(1 2 3)) +(apply + 1 2 '(3)) +(apply + '()) +]} + + +@defproc[(keyword-apply [proc procedure?] + [kw-lst (listof (cons/c keyword? any/c))] + [v any/c] ... + [lst list?]) + any]{ + +@guideintro["guide:apply"]{@scheme[keyword-apply]} + +Like @scheme[apply], but @scheme[kw-lst] supplies by-keyword arguments +in addition to the by-position arguments of the @scheme[v]s and +@scheme[lst]. The given @scheme[kw-lst] must be sorted using +@scheme[keyword<] on the @scheme[car] of each pair in the list, and no +keyword can appear twice in the @scheme[car]s of @scheme[kw-lst]. The +given @scheme[proc] must accept all of the keywords in +@scheme[kw-lst], and it must not require any other keywords; +otherwise, the @exnraise[exn:fail:contract]. + +@defexamples[ +(define (f x #:y y #:z [z 10]) + (list x y z)) +(keyword-apply f '((#:y . 2)) '(1)) +(keyword-apply f '((#:y . 2) (#:z . 3)) '(1)) +]} + +@defproc[(procedure-arity [proc procedure?]) + (or/c exact-nonnegative-integer? + arity-at-least? + (listof + (or/c exact-nonnegative-integer? + arity-at-least?)))]{ + +Returns information about the number of arguments accepted by +@scheme[proc] when called without keyword arguments. The result +@scheme[_a] is one of the following: + +@itemize{ + + @item{An exact non-negative integer, which means that the procedure + always accepts exactly @scheme[_a] arguments.} + + @item{A @scheme[arity-at-least] instance, which means that the + procedure takes @scheme[(arity-at-least-value _a)] or more + arguments.} + + @item{A list containing integers and @scheme[arity-at-least] + instances, which means that the procedure takes any number of + arguments that can match one of the elements of @scheme[_a].} + +} + +If a procedure requires at least one keyword argument, then +@scheme[procedure-arity] returns @scheme['()] for the procedure. + +@examples[ +(procedure-arity cons) +(procedure-arity list) +(arity-at-least? (procedure-arity list)) +(arity-at-least-value (procedure-arity list)) +(arity-at-least-value (procedure-arity (lambda (x . y) x))) +(procedure-arity (case-lambda [(x) 0] [(x y) 1])) +]} + +@defproc[(procedure-arity-includes? [proc procedure?] [k exact-nonnegative-integer?]) + boolean?]{ +Returns @scheme[#t] if the procedure can accept @scheme[k] arguments +when no keyword arguments are supplied, @scheme[#f] otherwise. + +@examples[ +(procedure-arity-includes? cons 2) +(procedure-arity-includes? display 3) +]} + +@defproc[(keyword-procedure-arity [proc procedure?]) + (values + (listof keyword?) + (or/c (listof keyword?) + false/c) + (or/c + exact-nonnegative-integer? + arity-at-least? + (listof + (or/c + exact-nonnegative-integer? + arity-at-least?))))]{ + +Similar to @scheme[keyword-procedure-arity], but for the case +that keywords are supplied to @scheme[proc]. The first result is +a list of keywords (sorted by @scheme[keyword<]) that are +required when applying @scheme[proc]. The second result is a list +of accepted keyword (sorted by @scheme[keyword<]), or @scheme[#f] +to mean that any keyword is accepted. The last result is as for +@scheme[procedure-arity] for the by-position arguments of +@scheme[proc], except that it is not @scheme['()] when keywords +are required.} + +@defproc[(make-keyword-procedure + [proc (((listof (cons/c keyword? any/c))) list? . ->* . any)]) + procedure?]{ + +Returns a procedure that accepts any number of arguments and all +keyword arguments (without requiring any keyword arguments). The +resulting procedure calls @scheme[proc], supplying to @scheme[proc] +all keyword arguments given in the original application as a list of +keyword--value pairs, sorted by @scheme[keyword<] on the keywords. All +by-position arguments supplied in the original application are +supplied to @scheme[proc] after the list for keyword arguments. + +@defexamples[ +(define show + (make-keyword-procedure (lambda (kw-args . rest) + (list kw-args rest)))) + +(show 1) +(show #:init 0 1 2 3 #:extra 4) +]} + +@defstruct[arity-at-least ([value nonnegative-exact-integer?])]{ +This structure type is used for the result of @scheme[procedure-arity].} diff --git a/collects/scribblings/reference/syntax.scrbl b/collects/scribblings/reference/syntax.scrbl index 7184f297c4..d4469c764b 100644 --- a/collects/scribblings/reference/syntax.scrbl +++ b/collects/scribblings/reference/syntax.scrbl @@ -27,25 +27,24 @@ Within such specifications, @item{@scheme[...+] indicates one or more repetitions of the preceding datum.} - @item{italic meta-identifier play the role of non-terminals; in + @item{italic meta-identifiers play the role of non-terminals; in particular, @itemize{ - @item{a meta-identifier that ends in @scheme[_id] stands for a - syntax-wrapped symbol.} + @item{a meta-identifier that ends in @scheme[_id] stands for an + identifier.} @item{a meta-identifier that ends in @scheme[_keyword] stands - for a syntax-wrapped keyword.} + for a keyword.} @item{a meta-identifier that ends with @scheme[_expr] stands for a sub-form that is expanded as an expression.} - @item{a meta-identifier that ends with @scheme[_body] stands - for a non-empty syntax-wrapped list of sub-forms; the - list is expanded as internal-definition sequence - followed by at least one expression (see - @secref["mz:intdef-body"] for details).} + @item{A meta-identifier that ends with @scheme[_body] stands + for a sub-form that is expanded in an + internal-definition context (see + @secref["mz:intdef-body"]).} }} } @@ -149,21 +148,21 @@ according to their order in the application form. @guideintro["guide:lambda"]{procedure expressions} -@defform/subs[(lambda formals* . body) +@defform/subs[(lambda gen-formals body ...+) ([formals (id ...) (id ...+ . rest-id) rest-id] - [formals* formals - (formal-arg ...) - (formal-arg ...+ . rest-id)] - [formal-arg id - [id default-expr] - (code:line keyword id) - (code:line keyword [id default-expr])])]{ + [gen-formals formals + (arg ...) + (arg ...+ . rest-id)] + [arg id + [id default-expr] + (code:line keyword id) + (code:line keyword [id default-expr])])]{ -Produces a procedure. The @scheme[formals*] determines the number of +Produces a procedure. The @scheme[gen-formals] determines the number of arguments that the procedure accepts. It is either a simple -@scheme[_formals], or one of the extended forms. +@scheme[formals], or one of the extended forms. A simple @scheme[_formals] has one of the following three forms: @@ -182,11 +181,11 @@ A simple @scheme[_formals] has one of the following three forms: arguments. All arguments are placed into a list that is associated with @scheme[rest-id].} -Besides the three @scheme[_formals] forms, a @scheme[formals*] can be -a sequence of @scheme[_formal-arg]s optionally ending with a +Besides the three @scheme[formals] forms, a @scheme[gen-formals] can be +a sequence of @scheme[arg]s optionally ending with a @scheme[rest-id]: -@specsubform[(formal-arg ...)]{ Each @scheme[formal-arg] has the following +@specsubform[(arg ...)]{ Each @scheme[arg] has the following four forms: @specsubform[id]{Adds one to both the minimum and maximum @@ -199,7 +198,7 @@ a sequence of @scheme[_formal-arg]s optionally ending with a @scheme[id] is associated with an actual argument by position, and if no such argument is provided, the @scheme[default-expr] is evaluated to produce a value associated with @scheme[id]. - No @scheme[formal-arg] with a @scheme[default-expr] can appear + No @scheme[arg] with a @scheme[default-expr] can appear before an @scheme[id] without a @scheme[default-expr] and without a @scheme[keyword].} @@ -215,41 +214,40 @@ a sequence of @scheme[_formal-arg]s optionally ending with a otherwise, the @scheme[default-expr] is evaluated to obtain a value to associate with @scheme[id].} - The position of a @scheme[_keyword] @scheme[formal-arg] in - @scheme[formals*] does not matter, but each specified + The position of a @scheme[_keyword] @scheme[arg] in + @scheme[gen-formals] does not matter, but each specified @scheme[_keyword] must be distinct.} -@specsubform[(formal-arg ...+ . rest-id)]{ Like the previous case, but +@specsubform[(arg ...+ . rest-id)]{ Like the previous case, but the procedure accepts any number of non-keyword arguments beyond its minimum number of arguments. When more arguments are provided than non-@scheme[_keyword] arguments among the - @scheme[formal-arg]s, the extra arguments are placed into a + @scheme[arg]s, the extra arguments are placed into a list that is associated to @scheme[rest-id].} -The @scheme[formals*] identifiers are bound in the @scheme[body]. (See -@secref["mz:intdef-body"] for information on @scheme[body] forms.) When +The @scheme[gen-formals] identifiers are bound in the @scheme[body]s. When the procedure is applied, a new location is created for each identifier, and the location is filled with the associated argument value. -If any identifier appears in @scheme[body] that is not one of the -identifiers in @scheme[formals*], then it refers to the same location +If any identifier appears in the @scheme[body]s that is not one of the +identifiers in @scheme[gen-formals], then it refers to the same location that it would if it appeared in place of the @scheme[lambda] expression. (In other words, variable reference is lexically scoped.) -When multiple identifiers appear in a @scheme[formals*], they must be +When multiple identifiers appear in a @scheme[gen-formals], they must be distinct according to @scheme[bound-identifier=?]. If the procedure procedure by @scheme[lambda] is applied to fewer or more arguments than it accepts, the @exnraise[exn:fail:contract]. If -@scheme[formals*] includes @scheme[keyword]s and an application +@scheme[gen-formals] includes @scheme[keyword]s and an application includes too few arguments before the keyword section, the same keyword in multiple positions, or a keyword that is not among the -@scheme[formals*] @scheme[_keyword]s, then the +@scheme[gen-formals] @scheme[_keyword]s, then the @exnraise[exn:fail:contract]. -The last expression in @scheme[body] is in tail position with respect -to the procedure body. +The last @scheme[body] expression is in tail position with respect to +the procedure body. @examples[ ((lambda (x) x) 10) @@ -260,9 +258,9 @@ to the procedure body. (f #:arg 2 1))) ]} -@defform[(case-lambda [formals . body] ...)]{ +@defform[(case-lambda [formals body ...+] ...)]{ -Produces a procedure. Each @scheme[[forms body]] +Produces a procedure. Each @scheme[[forms body ...+]] clause is analogous to a single @scheme[lambda] procedure; applying the @scheme[case-lambda]-generated procedure is the same as applying a procedure that corresponds to one of the clauses---the first procedure @@ -271,7 +269,7 @@ procedure accepts the given number of arguments, the @exnraise[exn:fail:contract]. Note that a @scheme[case-lambda] clause supports only -@scheme[formals], not the more general @scheme[_formals*] of +@scheme[formals], not the more general @scheme[_gen-formals] of @scheme[lambda]. That is, @scheme[case-lambda] does not directly support keyword and optional arguments. @@ -290,14 +288,13 @@ support keyword and optional arguments. @;------------------------------------------------------------------------ @section{Local Binding: @scheme[let], @scheme[let*], and @scheme[letrec]} -@defform*[[(let ([id val-expr] ...) . body) - (let proc-id ([id init-expr] ...) . body)]]{ +@defform*[[(let ([id val-expr] ...) body ...+) + (let proc-id ([id init-expr] ...) body ...+)]]{ The first form evaluates the @scheme[val-expr]s left-to-right, creates a new location for each @scheme[id], and places the values into the -locations. It then evaluates the @scheme[body], in which the -@scheme[id]s are bound. (See @secref["mz:intdef-body"] for information -on @scheme[body] forms.) The last expression in @scheme[body] is in +locations. It then evaluates the @scheme[body]s, in which the +@scheme[id]s are bound. The last @scheme[body] expression is in tail position with respect to the @scheme[let] form. The @scheme[id]s must be distinct according to @scheme[bound-identifier=?]. @@ -311,8 +308,8 @@ must be distinct according to @scheme[bound-identifier=?]. The second form evaluates the @scheme[init-expr]s; the resulting values become arguments in an application of a procedure -@scheme[(lambda (id ...) . body)], where @scheme[proc-id] is bound -within @scheme[body] to the procedure itself.} +@scheme[(lambda (id ...) body ...+)], where @scheme[proc-id] is bound +within the @scheme[body]s to the procedure itself.} @examples[ (let fac ([n 10]) @@ -321,12 +318,12 @@ within @scheme[body] to the procedure itself.} (* n (fac (sub1 n))))) ] -@defform[(let* ([id val-expr] ...) . body)]{ +@defform[(let* ([id val-expr] ...) body ...+)]{ Similar to @scheme[let], but evaluates the @scheme[val-expr]s one by one, creating a location for each @scheme[id] as soon as the value is availablek. The @scheme[id]s are bound in the remaining @scheme[val-expr]s -as well as the @scheme[body], and the @scheme[id]s need not be +as well as the @scheme[body]s, and the @scheme[id]s need not be distinct. @examples[ @@ -335,12 +332,12 @@ distinct. (list y x)) ]} -@defform[(letrec ([id val-expr] ...) . body)]{ +@defform[(letrec ([id val-expr] ...) body ...+)]{ Similar to @scheme[let], but the locations for all @scheme[id]s are created first and filled with @|undefined-const|, and all -@scheme[id]s are bound in all @scheme[val-expr]s as well as -@scheme[body]. The @scheme[id]s must be distinct according to +@scheme[id]s are bound in all @scheme[val-expr]s as well as the +@scheme[body]s. The @scheme[id]s must be distinct according to @scheme[bound-identifier=?]. @examples[ @@ -353,21 +350,21 @@ created first and filled with @|undefined-const|, and all (is-odd? 11)) ]} -@defform[(let-values ([(id ...) val-expr] ...) . body)]{ Like +@defform[(let-values ([(id ...) val-expr] ...) body ...+)]{ Like @scheme[let], except that each @scheme[val-expr] must produce as many values as corresponding @scheme[id]s. A separate location is created -for each @scheme[id], all of which are bound in the @scheme[body]. +for each @scheme[id], all of which are bound in the @scheme[body]s. @examples[ (let-values ([(x y) (quotient/remainder 10 3)]) (list y x)) ]} -@defform[(let*-values ([(id ...) val-expr] ...) . body)]{ Like +@defform[(let*-values ([(id ...) val-expr] ...) body ...+)]{ Like @scheme[let*], except that each @scheme[val-expr] must produce as many values as corresponding @scheme[id]s. A separate location is created for each @scheme[id], all of which are bound in the later -@scheme[val-expr]s and in the @scheme[body]. +@scheme[val-expr]s and in the @scheme[body]s. @examples[ (let*-values ([(x y) (quotient/remainder 10 3)] @@ -375,12 +372,12 @@ for each @scheme[id], all of which are bound in the later z) ]} -@defform[(letrec-values ([(id ...) val-expr] ...) . body)]{ Like +@defform[(letrec-values ([(id ...) val-expr] ...) body ...+)]{ Like @scheme[letrec], except that each @scheme[val-expr] must produce as many values as corresponding @scheme[id]s. A separate location is created for each @scheme[id], all of which are initialized to @|undefined-const| and bound in all @scheme[val-expr]s -and in the @scheme[body]. +and in the @scheme[body]s. @examples[ (letrec-values ([(is-even? is-odd?)