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)))))

Anonymous functions

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.

Optional, rest and keyword parameters

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)
#|kawa:13|# (addup 1 2 3)
#|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)
#|kawa:5|# (addup 1 2 3)
#|kawa:6|# (addup 1 2 3 4 5 6 7 8)

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:

(define (vector-3d #!key x y z)
  (make-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.