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