Application and Arguments Lists

When a procedure is called, the actual argument expressions are evaluated, and the resulting values becomes the actual argument list. This is then matched against the formal parameter list in the procedure definition, and (assuming they match) the procedure body is called.

Arguments lists

An argument list has three parts:

  • Zero or more prefix arguments, each of which is a value. These typically get bound to named required or optional formal parameters, but can also get bound to patterns.

  • Zero or more keyword arguments, each of which is a keyword (an identifier specified with keyword syntax) combined with a value. These are bound to either named keyword formal parameters, or bundled in with a rest parameter.

  • Zero or more postfix arguments, each of which is a value. These are usually bound to a “rest” formal parameter, which receives any remaining arguments.

    If there are no keyword arguments, then it ambiguous where prefix arguments end and where postfix arguments start. This is normally not a problem: the called procedure can split them up however it wishes.

Note that all keyword arguments have to be grouped together: It is not allowed to have a keyword argument followed by a plain argument followed by a keyword argument.

The argument list is constructed by evaluating each operand of the procedure-call in order:

expression

The expression is evaluated, yielding a single value that becomes a prefix or postfix argument.

keyword expression

The expression is evaluated. The resulting value combined with the keyword becomes a keyword argument.

@expression

The expression is evaluated. The result must be a sequence - a list, vector, or primitive array. The values of the sequence are appended to the resulting argument list. Keyword arguments are not allowed.

@:expression

The expression is evaluted. The result can be a sequence; a hash table (viewed as a collection of (keyword,value) pairs); or an explicit argument list object, which is a sequence of values or keyword arguments. The values and keyword arguments are appended to the resulting argument list, though subject to the restriction that keyword arguments must be adjacent in the resulting argument list.

Explicit argument list objects

Sometimes it is useful to create an argument list out of pieces, take argument lists apart, iterate over them, and generally treat an argument list as an actual first-class value.

Explicit argument list objects can take multiple forms. The simplest is a sequence: a list, vector, or primitive array. Each element of the list becomes a value in the resulting argument list.

(define v1 '(a b c))
(define v2 (int[] 10 11 12 13))
(list "X" @v1 "Y" @v2 "Z")
  ⇒ ("X" a b c "Y" 10 11 12 13 "Z")

Things get more complicated once keywords are involved. An explicit argument list with keywords is only allowed when using the @: splicing form, not the @ form. It can be either a hash table, or the types arglist or argvector.

Design note: An argument list with keywords is straightforward in Common Lisp and some Scheme implementations (including order versions of Kawa): It’s just a list some of whose car cells are keyword objects. The problem with this model is neither a human or the compiler can reliably tell when an argument is a keyword, since any variable might have been assigned a keyword. This limits performance and error checking.

A hash table (anything the implements java.util.Map) whose keys are strings or keyword objects is interpreted as a sequence of keyword arguments, using the hash-table keys and values.

Type: argvector

Constructor: argvector operand*

List of arguments represented as an immutable vector. A keyword argument takes two elements in this vector: A keyword object, followed by the value.

(define v1 (argvector 1 2 k1: 10 k2: 11 98 99))
(v1 4) ⇒ 'k2
(v1 5) ⇒ 11

When v1 is viewed as a vector it is equivalent to (vector 1 2 'k1: 10 'k2: 11 98 99). (Note in this case the keywords need to be quoted, since the vector constructor does not take keyword arguments.) However, the argvector “knows” which arguments are actually keyword arguments, and can be examined using the (kawa arglist) library discussed below:

(arglist-key-count (argvector 1 x: 2 3)) ⇒ 1
(arglist-key-count (argvector 1 'x: 2 3)) ⇒ 0
(arglist-key-count (vector 1 'x: 2 3)) ⇒ 0

In this case:

(fun 'a @:v1)

is equivalent to:

(fun 'a 1 2 k1: 10 k2: 11 98 99)

Type: arglist

Constructor: arglist operand*

Similar to argvector, but compatible with list. If there are no keyword arguments, returns a plain list. If there is at least one keyword argument creates a special gnu.mapping.ArgListPair object that implements the usual list properties but internally wraps a argvector.

Argument list library

(import (kawa arglist))

In the following, args is an arglist or argvector (or in general any object that implement gnu.mapping.ArgList). Also, args can be any sequence, in which case it behaves like an argvector that has no keyword arguments.

Procedure: arglist-walk args proc

Call proc once, in order, for each argument in args. The proc is called with two arguments, corresponding to (arglist-key-ref args i) and (arglist-arg-ref args i) for each i from 0 up to (arglist-arg-count args) (exclusive). I.e. the first argument is either #!null or the keyword (as a string); the second argument is the corresponding argument value.

(define (print-arguments args #!optional (out (current-output-port)))
  (arglist-walk args
                (lambda (key value)
                  (if key (format out "key: ~a value: ~w~%" key value)
                      (format out "value: ~w~%" value)))))

Procedure: arglist-key-count args

Return the number of keyword arguments.

Procedure: arglist-key-start args

Number of prefix arguments, which is the number of arguments before the first keyword argument.

Procedure: arglist-arg-count args

Return the number of arguments. The count includes the number of keyword arguments, but not the actual keywords.

(arglist-arg-count (arglist 10 11 k1: -1 19)) ⇒ 4

Procedure: arglist-arg-ref args index

Get the index’th argument value. The index counts keyword argument values, but not the keywords themselves.

(arglist-arg-ref (arglist 10 11 k1: -1 19) 2) ⇒ -1
(arglist-arg-ref (arglist 10 11 k1: -1 19) 3) ⇒ 19

Procedure: arglist-key-ref args index

The index counts arguments like arglist-arg-ref does. If this is a keyword argument, return the corresponding keyword (as a string); otherwise, return #!null (which counts as false).

(arglist-key-ref (argvector 10 11 k1: -1 k2: -2 19) 3) ⇒ "k2"
(arglist-key-ref (argvector 10 11 k1: -1 k2: -2 19) 4) ⇒ #!null

Procedure: arglist-key-index args key

Search for a keyword matching key (which must be an interned string). If there is no such keyword, return -1. Otherwise return the keyword’s index as an argument to arglist-key-ref.

Procedure: arglist-key-value args key default

Search for a keyword matching key (which must be an interned string). If there is no such keyword, return the default. Otherwise return the corresponding keyword argument’s value.

Apply procedures

Procedure: apply proc argi* argrest

Argrest must be a sequence (list, vector, or string) or a primitive Java array. (This is an extension over standard Scheme, which requires that args be a list.) Calls the proc (which must be a procedure), using as arguments the argi... values plus all the elements of argrest.

Equivalent to: (proc argi* @argrest).

Syntax: constant-fold proc arg1 ...

Same as (proc arg1 ...), unless proc and all the following arguments are compile-time constants. (That is: They are either constant, or symbols that have a global binding and no lexical binding.) In that case, proc is applied to the arguments at compile-time, and the result replaces the constant-fold form. If the application raises an exception, a compile-time error is reported. For example:

(constant-fold vector 'a 'b 'c)

is equivalent to (quote #(a b c)), assuming vector has not been re-bound.