# (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 `var`s 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 `var`s, then the remaining `var`s taken their values from their corresponding `default`s, evaluated in an unspecified order. Unused `default`s 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 `var`s 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 `var`s 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 363 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 363 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 `var`s in scope.

Example:

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

`=> (1 5 15)`