To declare a new function use define
,
which has the following form:
(define (function-name
parameter-names
)body
)
This creates a new function named function-name
,
which takes parameter-names
as parameters.
When the function is called, the parameter-names
are initialized with the actual arguments. Then body
is evaluated, and its value becomes the result of the call.
For example, in the factorial
function we looked at recently,
the function-name
is factorial
,
and the parameter-names
is x
:
(define (factorial x) (if (< x 1) 1 (* x (factorial (- x 1)))))
An anonymous function is simply a function which does not have a name. We define an anonymous function using a lambda expression, which has the following form:
(lambda (parameter-names
)body
)
The lambda expression has the parameter-names
and body
of a
function, but it has no name. What is the point of this?
An important example is creating a function to act on a list, perhaps using
map
. The map
function takes two parameters: the first is a
function which takes a value and returns a value; the second is a list. Here,
we want to double every number in the list.
The usual way of doing this is to create a named function, called
double
, and then apply it to a list:
#|kawa:1|#(define (double x)
#|.....2|#(* 2 x))
#|kawa:3|#(map double (list 1 2 3 4 5))
(2 4 6 8 10)
Instead, anonymous functions make it easy to create a function to work on a list, without having to define it in advance:
#|kawa:4|#(map (lambda (x) (* 2 x)) (list 1 2 3 4 5))
(2 4 6 8 10) #|kawa:5|#(define y 3)
#|kawa:6|#(map (lambda (x) (* x y)) (list 1 2 3 4 5))
(3 6 9 12 15)
The first example shows the double example rewritten as an anonymous function.
The second example shows how the anonymous function can be changed to
fit the place in which it is used: here, the value of y
determines the
value by which the list values are multiplied.
Notice that we can name our anonymous functions, in just the same way we
name any value in Kawa, using define
:
(define double (lambda (n) (* 2 n)))
although more frequently we use the short-hand for defining functions, which we have already met:
(define (double n) (* 2 n))
Anonymous functions are “first-class values” in Kawa, and can be passed to
other functions as arguments (like we did with map
), and they can even
be created and returned by functions as results.
You can declare a function that takes optional arguments, or a variable number of arguments. You can also use keyword parameters.
The following function illustrates the use of optional arguments. The function
identifies an optional argument z
: if the function is called with 3 arguments, z
will be bound to the third value, otherwise it will be #f
.
(define (addup x y #!optional z) (if z (+ x y z) (+ x y)))
The following examples show addup
applied to 2, 3 and invalid arguments. It is an error to
pass just one argument or more than three: x
and y
are compulsory, but z
is
optional.
#|kawa:12|#(addup 1 2)
3 #|kawa:13|#(addup 1 2 3)
6 #|kawa:14|#(addup 1)
/dev/stdin:14:1: call to 'addup' has too few arguments (1; min=2, max=3) #|kawa:15|#(addup 1 2 3 4)
/dev/stdin:15:1: call to 'addup' has too many arguments (4; min=2, max=3)
In this example, a better way to define the function would be to include a
default value for z
, for when its value is not given by the caller.
This is done as follows, with the same behavior as above:
(define (addup x y #!optional (z 0)) (+ x y z))
You can include as many optional parameters as you wish, after the #!optional
.
Rest arguments are an alternative way to pass an undefined number of
arguments to a function. Here is addup
written with rest arguments,
notice the variable name after the . (dot):
(define (addup x y . args) (+ x y (apply + args)))
The args
are simply a list of all remaining values. The following now all work, as the
function only requires a minimum of two numbers:
#|kawa:4|#(addup 1 2)
3 #|kawa:5|#(addup 1 2 3)
6 #|kawa:6|#(addup 1 2 3 4 5 6 7 8)
36
An alternative way to identify the rest args is with #!rest
:
(define (addup x y #!rest args) (+ x y (apply + args)))
Finally, it can be useful to identify parameters by name and, for this, Kawa provides keyword arguments. Consider the following function:
#|kawa:38|#(define (vector-3d #!key x y z)
#|.....39|#(vector x y z))
#|kawa:40|#(vector-3d #:x 2 #:z 3 #:y 4)
#(2 4 3)
vector-3d
is defined with three keyword arguments: x
, y
, and z
. When the
function is called, we identify the name for each value by writing #:
at the start of the name.
This allows us to write the arguments in any order. Keyword parameters can also be given default
values, as with optional parameters. Keyword parameters with no default value, and no value in the caller,
will get the value #f
.
In the caller, keywords are symbols with #:
at the front (or :
at
the end): read more here.
All these extended types of arguments are available both for “named” and for “anonymous” functions. Optional, rest and keyword arguments can be mixed together, along with the usual arguments. For details read more here.