Functions

Functions are another fundamental programming language construct. Ki's functions should be familiar to anyone who's used a mainstream language.

fn add(x: int, y: int): (int) {
    return(x + y)
}

Functions can return multiple values:

fn split_names(person: &Person): (str, str) {
    return(p.first_name, p.last_name)
}

You can pass functions to functions; they're first-class types.

fntype Operator(x: num, y: num): (num)

type Adder(fntype) {
  :call fn(x: num, y: num): (num) {
    return(x + y)
  }
}
type Subtracter(fntype) {
  :call fn(x: num, y: num): (num) {
    return(x - y)
  }
}


fn calculate(op: Operator, arg1: int, arg2: int): (int) {
    return(op(arg1, arg2))
}

fn main() {
    val adder: fn(int x, int y) (int) {
        return x + y
    }
    val subtracter: fn(int x, int y) (int) {
        return x - y
    }
    val multiplier: fn(int x, int y) (int) {
        return x * y
    }
    val divider: fn(int x, int y) (int) {
        return x / y
    }
    val exponenter: fn(int x, int y) (int) {
        return x ** y
    }
    val modder: fn(int x, int y) (int) {
        return x % y
    }

    val a: 9
    val b: 2

    echo("add: ${calculate(adder, a, b)}")
    echo("subtract: ${calculate(subtracter, a, b)}")
    echo("multiply: ${calculate(multiplier, a, b)}")
    echo("divide: ${calculate(divider, a, b)}")
    echo("exponent: ${calculate(exponenter, a, b)}")
    echo("modulus: ${calculate(modder, a, b)}")
}

This is a little verbose. Let's indulge in a little code golf.

fntype Operator(int x, int y) (int)

fn calculate(Operator op, int arg1, int arg2) (int) {
    return op(arg1, arg2)
}

fn main() {
    val adder: Operator {
      while y > 0 {
        x++
        y--
      }

      return x
    }
    val subtracter: Operator {return x - y}
    val multiplier: Operator {return x * y}
    val divider: Operator {return x / y}
    val exponenter: Operator {return x ** y}
    val modder: Operator {return x % y}

    val a: 9
    val b: 2

    echo("add: ${calculate(adder, a, b)}")
    echo("subtract: ${calculate(subtracter, a, b)}")
    echo("multiply: ${calculate(multiplier, a, b)}")
    echo("divide: ${calculate(divider, a, b)}")
    echo("exponent: ${calculate(exponenter, a, b)}")
    echo("modulus: ${calculate(modder, a, b)}")
}

By using fntype, we can simply define the body of a function and rely on the information given in the fntype declaration to fill in the blanks for the compiler. We also consolidated the "args" and "return" blocks in the fntype declaration using parentheses. This works similarly for composite type declarations.

Using fntype is better, but we could skip defining those functions altogether (and be a little higher-level in our definition of "adder"):

fntype Operator(int x, int y) (int)

fn calculate(Operator op, int arg1, int arg2) (int) {
    return op(arg1, arg2)
}

fn main() {
    val a: 9
    val b: 2

    echo("add: ${calculate(Operator {return x + y}, a, b)}")
    echo("sub: ${calculate(Operator {return x - y}, a, b)}")
    echo("mul: ${calculate(Operator {return x * y}, a, b)}")
    echo("div: ${calculate(Operator {return x / y}, a, b)}")
    echo("pow: ${calculate(Operator {return x ** y}, a, b)}")
    echo("mod: ${calculate(Operator {return x % y}, a, b)}")
}