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.
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.
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.
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) ⇒ 11When
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 thevector
constructor does not take keyword arguments.) However, theargvector
“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)) ⇒ 0In this case:
(fun 'a @:v1)is equivalent to:
(fun 'a 1 2 k1: 10 k2: 11 98 99)
Constructor: arglist
operand
*
Similar to
argvector
, but compatible withlist
. If there are no keyword arguments, returns a plain list. If there is at least one keyword argument creates a specialgnu.mapping.ArgListPair
object that implements the usuallist
properties but internally wraps aargvector
.
(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 inargs
. Theproc
is called with two arguments, corresponding to(arglist-key-ref
andargs
i
)(arglist-arg-ref
for eachargs
i
)i
from 0 up to(arglist-arg-count
(exclusive). I.e. the first argument is eitherargs
)#!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. Theindex
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 likearglist-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 toarglist-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 thedefault
. Otherwise return the corresponding keyword argument’s value.
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 thatargs
be a list.) Calls theproc
(which must be a procedure), using as arguments theargi
... values plus all the elements ofargrest
.Equivalent to:
(
proc
argi
*@
argrest
)
.
Syntax: constant-fold
proc
arg1
...
Same as
(
, unlessproc
arg1
...)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 theconstant-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))
, assumingvector
has not been re-bound.