Lambdas
Also known as: lambda anonymous function closure arrow function callback inline function
● Beginner4 minBefore this: FunctionsSpec 3.3
A lambda is a small function without a name, written where it is used: x => x * 2.
fn main() {
const double = x => x * 2
print(double(21))
const prices = [120, 40, 300]
print(prices.map(p => p + 10))
let limit = 10
const over = n => n > limit
limit = 0
print(over(5))
}The program ran and printed:
kavach run lambda.kav
42[130, 50, 310]false
x => x * 2takesxand givesx * 2. Its types come from where it is used.- Lambdas shine as arguments:
prices.map(p => p + 10)adds 10 to every price. - A lambda keeps a copy of the outside values it uses: changing
limitlater doesn't changeover.
Because it is a copy, a lambda that changes an outside variable changes only its own copy. Other languages would print 2 here; Kavach prints 0, and warns where it happens:
fn main() {
let count = 0
const add = () => {
count += 1
}
add()
add()
print(count)
}The compiler warned, then the program ran:
kavach run lambda-copy.kav
warning: `count` is a copy inside this lambda: the change doesn't reach the outside. Give the value back instead, or use `Shared` to share it (spec 3.3)0
A change to the copy, with =, += or a method that changes a value (push, add, remove), lasts only for that call: each call starts again from the copy taken when the lambda was made. So each sum below is just its own item, each call's log is [0] and one item, and the real total and log never change:
fn main() {
let total = 0
const sums = [1, 2, 3].map(x => {
total += x // warning: `total` is a copy inside this lambda
total
})
print(sums)
print(total)
let log = [0]
const lens = [1, 2, 3].map(x => {
log.push(x) // warning: `log` is a copy inside this lambda
log.len()
})
print(lens)
print(log)
}The compiler warned, then the program ran:
kavach run lambda-change.kav
warning: `total` is a copy inside this lambda: the change doesn't reach the outside. Give the value back instead, or use `Shared` to share it (spec 3.3)warning: `log` is a copy inside this lambda: the change doesn't reach the outside. Give the value back instead, or use `Shared` to share it (spec 3.3)[1, 2, 3]0[2, 2, 2][0]
To count from inside, give the new value back from the lambda, or share it on purpose with Shared.
try inside a lambda
A try or fail inside a lambda belongs to the lambda: it ends that call of the lambda with the error, so the lambda is a function that can fail. The error never jumps out of the function the lambda was written in, since the lambda may be called long after that function has returned.
type ShopError = TooMany(qty: Int)
fn check(qty: Int) -> Int ! ShopError {
if qty > 10 {
fail ShopError.TooMany(qty: qty)
}
qty
}
fn main() {
const doubled = q => try check(q) * 2
print(doubled(3) catch _ => 0)
const big = doubled(50) catch e => match e {
ShopError.TooMany(qty) => -qty,
}
print(big)
}The program ran and printed:
kavach run lambda-try.kav
6-50
Each call of doubled is a call that can fail, so it gets a catch. Given to a method such as map, such a lambda makes the map call the one that can fail: try shows how.
A call that can fail, written in a lambda with no try, is an error that says where the error would go:
fn positive(n: Int) -> Bool ! String {
if n == 0 {
fail "zero"
}
n > 0
}
fn main() {
const kept = [3, -1, 2].filter(n => positive(n))
print(kept)
}The compiler refused the program:
kavach run lambda-no-try.kav
error: `positive` can fail: write `try` before it to pass the error on from this lambda, which the call that takes the lambda then passes on (spec 3.3), or handle it here with `catch` or `match`return inside a lambda
A return inside a lambda belongs to the lambda too: it ends that call of the lambda with its value, as in Rust, Swift and JavaScript, and the function the lambda was written in goes on:
fn main() {
const prices = [120, 0, -5, 40]
const labels = prices.map(p => {
if p < 0 {
return "refund" // this item's label; `map` goes on with the next
}
if p == 0 {
return "free"
}
"paid {p}"
})
print(labels)
print("main goes on")
}The program ran and printed:
kavach run lambda-return.kav
["paid 120", "free", "refund", "paid 40"]main goes on
Its value has the type of the lambda's last value:
fn main() {
const label = (p: Int) => {
if p == 0 {
return 0
}
"paid {p}"
}
print(label(5))
}The compiler refused the program:
kavach run lambda-return-type.kav
error: this lambda's `return` gives `Int`, but its last value is `String`