R5RS’s rest arguments are indeed useful and very general, but they
often aren’t the most appropriate or efficient means to get the job
done. For example, lambda*
is a much better solution to the
optional argument problem than lambda
with rest arguments.
Likewise, case-lambda
works well for when you want one
procedure to do double duty (or triple, or ...), without the penalty
of consing a rest list.
For example:
(define (make-accum n) (case-lambda (() n) ((m) (set! n (+ n m)) n))) (define a (make-accum 20)) (a) ⇒ 20 (a 10) ⇒ 30 (a) ⇒ 30
The value returned by a case-lambda
form is a procedure which
matches the number of actual arguments against the formals in the
various clauses, in order. The first matching clause is selected, the
corresponding values from the actual parameter list are bound to the
variable names in the clauses and the body of the clause is evaluated.
If no clause matches, an error is signaled.
The syntax of the case-lambda
form is defined in the following
EBNF grammar. Formals means a formal argument list just like
with lambda
(see Lambda: Basic Procedure Creation).
<case-lambda> --> (case-lambda <case-lambda-clause>*) --> (case-lambda <docstring> <case-lambda-clause>*) <case-lambda-clause> --> (<formals> <definition-or-command>*) <formals> --> (<identifier>*) | (<identifier>* . <identifier>) | <identifier>
Rest lists can be useful with case-lambda
:
(define plus (case-lambda "Return the sum of all arguments." (() 0) ((a) a) ((a b) (+ a b)) ((a b . rest) (apply plus (+ a b) rest)))) (plus 1 2 3) ⇒ 6
Also, for completeness. Guile defines case-lambda*
as well,
which is like case-lambda
, except with lambda*
clauses.
A case-lambda*
clause matches if the arguments fill the
required arguments, but are not too many for the optional and/or rest
arguments.
Keyword arguments are possible with case-lambda*
as well, but
they do not contribute to the “matching” behavior, and their
interactions with required, optional, and rest arguments can be
surprising.
For the purposes of case-lambda*
(and of case-lambda
, as a
special case), a clause matches if it has enough required
arguments, and not too many positional arguments. The required
arguments are any arguments before the #:optional
, #:key
,
and #:rest
arguments. Positional arguments are the
required arguments, together with the optional arguments.
In the absence of #:key
or #:rest
arguments, it’s easy to
see how there could be too many positional arguments: you pass 5
arguments to a function that only takes 4 arguments, including optional
arguments. If there is a #:rest
argument, there can never be too
many positional arguments: any application with enough required
arguments for a clause will match that clause, even if there are also
#:key
arguments.
Otherwise, for applications to a clause with #:key
arguments (and
without a #:rest
argument), a clause will match there only if
there are enough required arguments and if the next argument after
binding required and optional arguments, if any, is a keyword. For
efficiency reasons, Guile is currently unable to include keyword
arguments in the matching algorithm. Clauses match on positional
arguments only, not by comparing a given keyword to the available set of
keyword arguments that a function has.
Some examples follow.
(define f (case-lambda* ((a #:optional b) 'clause-1) ((a #:optional b #:key c) 'clause-2) ((a #:key d) 'clause-3) ((#:key e #:rest f) 'clause-4))) (f) ⇒ clause-4 (f 1) ⇒ clause-1 (f) ⇒ clause-4 (f #:e 10) clause-1 (f 1 #:foo) clause-1 (f 1 #:c 2) clause-2 (f #:a #:b #:c #:d #:e) clause-4 ;; clause-2 will match anything that clause-3 would match. (f 1 #:d 2) ⇒ error: bad keyword args in clause 2
Don’t forget that the clauses are matched in order, and the first
matching clause will be taken. This can result in a keyword being bound
to a required argument, as in the case of f #:e 10
.