(chibi optional)

Syntax to support optional and named keyword arguments. let-optionals[*] is originally from SCSH, and let-keywords[*] derived from Gauche.

(let-optionals ls ((var default) ... [rest]) body ...)

Binding construct similar to let. The vars are bound to fresh locations holding values taken in order from the list ls, body is evaluated in the resulting environment, and the value(s) of the last expression of body returned. If the length of ls is shorter than the number of vars, then the remaining vars taken their values from their corresponding defaults, evaluated in an unspecified order. Unused defaults are not evaluated. If a final rest var is specified, then it is bound to any remaining elements of ls beyond the length of ls, otherwise any extra values are unused.

ls is evaluated only once. It is an error if any default mutates ls.

Typically used on the dotted rest list at the start of a lambda, let-optionals is more concise and more efficient than case-lambda for simple optional argument uses.

Example:

(define (copy-port . o)
  (let-optionals o ((in (current-input-port))
                    (out (current-output-port))
                    (n-bytes #f))
    (do ((i 0 (+ i 1))
         (n (read-u8 in) (read-u8 in)))
        ((or (and n-bytes (>= i n-bytes))
             (eof-object? b)))
      (write-u8 b out)))

Example:

(let-optionals '(0) ((a 10) (b 11) (c 12))
  (list a b c))
=> (0 11 12)

(let-optionals* ls ((var default) ... [rest]) body ...)

let* equivalent to let-optionals. Any required default values are evaluated in left-to-right order, with all preceding vars in scope.

(opt-lambda ((var default) ... [rest]) body ...)

Shorthand for

(lambda (required ... . o)
  (let-optionals o ((var default) ... [rest])
     body ...))

(opt-lambda* ((var default) ... [rest]) body ...)

Variant of opt-lambda which binds using let-optionals*.

(define-opt (name (var default) ... [rest]) body ...)

Shorthand for

(define name (opt-lambda (var default) ... [rest]) body ...)

(define-opt* (name (var default) ... [rest]) body ...)

Shorthand for

(define name (opt-lambda* (var default) ... [rest]) body ...)

(keyword-ref ls key [default])

Search for the identifier key in the list ls, treating it as a property list of the form (key1 val1 key2 val2 ...), and return the associated val. If not found, return default, or #f.

(keyword-ref* ls key default)

Macro equivalent of keyword-ref, where default is only evaluated if key is not found.

(let-keywords ls ((var [keyword] default) ... [rest]) body ...)

Analogous to let-optionals, except instead of binding the vars by position they are bound by name, by searching in ls with keyword-ref*. If an optional keyword argument is provided it must be an identifier to use as the name, otherwise var is used, appending a ":" (colon). If the name is not found, var is bound to default, even if unused names remain in ls.

Keyword arguments have precedence in CommonLisp, DSSSL, and SRFI 89. However, unlike these systems you cannot mix optional and keyword arguments.

If an optional trailing identifier rest is provided, it is bound to the list of unused arguments not bound to any var. This is useful for chaining together keyword argument procedures - you can extract just the arguments you need and pass on the rest to another procedure. The rest usage is similar to Python's **args (again predated by CommonLisp and DSSSL).

Note R7RS does not have a disjoint keyword type or auto-quoting syntax for keywords - they are simply identifiers (though no type checking is performed). Thus when passing keyword arguments they must be quoted (or otherwise dynamically evaluated).

Example:

(define (make-person . o)
  (let-keywords o ((name "John Doe")
                   (age 0)
                   (occupation job: 'unemployed))
    (vector name age occupation)))

(list (make-person)
      (make-person 'name: "Methuselah" 'age: 969)
      (make-person 'name: "Dr. Who" 'job: 'time-lord 'age: 1500))
ERROR on line 365 of file lib/chibi/doc.scm: immutable binding: make-person

Example:

(let-keywords '(b: 2 a: 1 other: 9)
    ((a 0) (b 0) (c 0) rest)
  (list a b c rest))
=> (1 2 0 (other: 9))

Example:

(define (auth-wrapper proc)
  (lambda o
    (let-keywords o ((user #f)
                     (password #f)
                     rest)
      (if (authenticate? user password)
          (apply proc rest)
          (error "access denied")))))

((auth-wrapper make-payment) 'user: "bob" 'password: "5ecret" 'amount: 50)
ERROR on line 365 of file lib/chibi/doc.scm: immutable binding: auth-wrapper

(let-keywords* ls ((var [keyword] default) ... [rest]) body ...)

let* equivalent to let-keywords. Any required default values are evaluated in left-to-right order, with all preceding vars in scope.

Example:

(let-keywords* '(b: 5)
    ((a 1) (b (* a 2)) (c (* b 3)))
  (list a b c))
=> (1 5 15)