Using functions in Kaya

Functions are the basic components of Kaya programs. They are defined by the following general scheme (the expression may be made up of many statements):

ReturnType functionName(arg1Type arg1name, ...) {
    expression;
}

A simple example is this function, which totals its arguments:

Int total(Int a, Int b, Int c) {
    return a+b+c;
}

A more complex example, which also demonstrates recursion (functions calling themselves) is the following function, which finds a number in the fibonacci sequence in an inefficient way.

Int fib(Int x) {
    if (x == 0 || x == 1) {
	return 1;
    } else {
        return fib(x-2)+fib(x-1);
    }
}

Functions are 'first-class' objects, and so have types and can be passed as variables. For example, the type of fib is Int(Int) and the type of total is Int(Int,Int,Int). The following function tests if all elements of an array match a user-supplied condition.

Bool allMatch([Int] xs, Bool(Int) test) {
// allMatch (hopefully obviously) has the type Bool([Int],Bool(Int))
    for x in xs {
        if (!test(x)) {
            return false;
        } 
    }
    return true;
}

This function could be used like this:

Void main() {
    allMatch([1,3,9,5,4,11],odd);
}

Bool odd(Int num) {
    return (num % 2 != 0);
}

Constant functions

For convenience, functions that take no arguments can have the brackets omitted. This is intended to allow you to represent constant values.

program piprog;

import IO; // For the "get" and "stdin" functions.

Float Pi {
    return 3.14159265358979;
}

Void main {
    putStr("Enter a radius: ");
    r = Float(get(stdin));
    putStrLn("Area is " + String(Pi*r*r));
}

Note that

stdin is another constant function, which returns the standard input stream. Running this program gives:
$ ./piprog
Enter a radius: 5
Area is 78.5398
$ 

A further shorthand for constant functions is

Float Pi = 3.141592653589793;

This is not a global variable, as it cannot be modified.

Function parameters

Function parameters are normally passed as a shallow copy (i.e. the parameter itself is copied, but references inside are not. Therefore:

Void increment(Int a) {
    a = a+1;
    putStrLn("a = "+a);
}

Void main() {
    a = 5;
    increment(a);
    putStrLn("a = "+a);
}

/* output is
a = 6
a = 5
*/

Incrementing the variable inside the function does not affect its value outside the function. However, because it's only a shallow copy:

Void increment([Int] a) {
    a[0] += 1;
    putStrLn("a[0] = "+a[0]);
}

Void main() {
    a = [5];
    increment(a);
    putStrLn("a[0] = "+a[0]);
}

/* output is
a[0] = 6
a[0] = 6
*/

To avoid modifying the values 'inside' a parameter, you can use the copy function from the standard prelude to make a deep copy of the parameter. (Warning: this can be very slow for complex data structures, and is usually unnecessary)

Void increment([Int] a) {
    a[0] += 1;
    putStrLn("a[0] = "+a[0]);
}

Void main() {
    a = [5];
    increment(copy(a));
    putStrLn("a[0] = "+a[0]);
}

/* output is
a[0] = 6
a[0] = 5
*/

Conversely, if you would like the function to be able to completely modify its parameters, you can use the var keyword to pass by reference.

Void increment(var Int a) {
    a = a+1;
    putStrLn("a = "+a);
}

Void main() {
    a = 5;
    increment(a);
    putStrLn("a = "+a);
}

/* output is
a = 6
a = 6
*/

Reserved names

Function names beginning with two underscores (e.g. __start) are reserved for Kaya templates and should not be used in program or module code.

Exercises

  1. Write a function that takes a single integer as a parameter, and returns the factorial of that integer (factorial N = 1*2*...*N) Optionally, write both recursive and non-recursive versions, and throw an appropriate Exception if the parameter is negative.
  2. Modify the piprog program above so it also gives the circumference of the circle, and gives a useful error message if the input isn't a sensible radius.
kaya@kayalang.org | Last modified 13 July 2009 | Supported by Durham CompSoc | Powered by Kaya