func double(n: Int): Int { return n * 2 }
proc main(): void {
f := double
echo "{f(21)} {map([1, 2, 3], double)}"
}type Msg {
Changed { text: Str }
}
func add(a: Int, b: Int): Int { return a + b }
proc main(): void {
mut n := 5
addN := add(n, _) // _ becomes the parameter; n is taken now
n = 100
echo addN(10) // 15 — what was supplied was kept as it was
make := Msg.Changed(_) // a function that builds the variant
echo make("typed")
}func double(n: Int): Int { return n * 2 }
func shout(s: Str): Str { return s + "!" }
func applyTwice(f: func(Int): Int, n: Int): Int { // as a parameter
return f(f(n))
}
func picked(loud: Bool): func(Str): Str { // as a return
return shout
}
proc main(): void {
func(Int): Int step = double // as a variable's type
echo applyTwice(step, 5)
echo picked(true)("hive")
}proc grow(names: mut Str[dyn], name: Str): void {
append(names, name)
}
proc twice(step: proc(mut Str[dyn]): void, names: mut Str[dyn]): void {
step(names)
step(names)
}
proc main(): void {
mut Str[dyn] names = ["ada"]
twice(grow(_, "bee"), names) // the mutex stays a hole
echo names
}proc third(v: Str[3]): void { echo v[2] }
proc anyLength(v: Str[]): void { echo len(v) }
proc callWithThree(f: proc(Str[3]): void): void {
f(["a", "b", "c"])
}
proc main(): void {
callWithThree(third) // fine: the slot promises three
callWithThree(anyLength) // fine: any length takes three too
// proc(Str[]): void g = third // compile error: g may be called with one
}A callable is a value, made one of three ways:
f := double;_ holes: add(5, _) is a function of one argument, each hole becoming a parameter in the order written. What is supplied is worked out once, where it is written, and kept: changing the variable afterwards changes nothing the function holds;Msg.Changed(_) is a function that builds the variant. Msg.Changed on its own is not a value.Its type is written like a declaration with the names dropped — func(Int): Int, proc(mut Str[dyn], Str): void — and it serves as a parameter, a return type and a variable's own type. A func value fills a proc slot, never the other way round, and a call through any function value is held to its type: how many arguments and what each is, by position, since a value's parameters have no names. A mut position in a partial application stays a hole, because what is supplied is kept as a copy that writes would never reach.
A function value fills a slot only when it takes everything the slot's calls can hand it: a proc(Str[3]): void cannot go where a proc(Str[]): void is wanted, since that slot may be called with any length, while the reverse is fine. A builtin, a library call and a generic callable cannot become values at all — wrap one in a func of your own.