Lambdas
Write small functions on the spot, pass them around, and let them remember the variables around them.
A lambda is a function without a name, written right where it's needed. You've already used them with arrays: n -> n * 2 is a lambda.
Writing a lambda#
The short form is parameters, ->, and an expression. With one parameter whose type the compiler can work out, that's all you need:
int[] numbers = [1, 2, 3, 4]
log numbers.map(n -> n * 10)
log numbers.filter(n -> n > 2)The compiler knows numbers holds ints, so it knows n is an int. When it can't tell, or when you want to be explicit, start the lambda with def and write the types out the same way as for a named function:
auto add = def(int a, int b) int -> a + b
log add(2, 3)Lambdas with more to do#
For more than one expression, give it a block:
auto describe = def(int n) string -> (
if n < 0 return "negative"
if n == 0 return "zero"
return "positive"
)
log [-2, 0, 5].map(describe)Sorting by something#
sortBy takes a lambda that picks what to sort on:
object[] people = [
{name: "Linus", age: 54},
{name: "Ada", age: 36},
{name: "Grace", age: 85}
]
log people.sortBy(p -> p.age).map(p -> p.name)
log people.sortBy(p -> p.name).map(p -> p.name)sortBy also takes a field name as a string: people.sortBy("age").
Remembering variables#
A lambda can use the variables around the place it was written, and it keeps them even after that code has finished. This is called a closure:
def makeCounter() def() int (
int count = 0
return def() int -> (
count++
return count
)
)
auto next = makeCounter()
log next()
log next()
log next()
auto other = makeCounter()
log other()Each call to makeCounter gets its own count, and the lambda it returns holds on to it.
Functions that make functions#
Because lambdas remember their surroundings, a function can build a custom function and hand it back:
def multiplier(int factor) def(int) int -> def(int n) int -> n * factor
auto triple = multiplier(3)
auto tenfold = multiplier(10)
log triple(7)
log tenfold(7)
log [1, 2, 3].map(triple)Naming function types#
A function type such as def(int) int can be named with type, which helps when it appears in several places:
type Rule = def(string) boolean
def check(string text, Rule[] rules) boolean -> rules.every(rule -> rule(text))
Rule notEmpty = def(string s) boolean -> s.len > 0
Rule short = def(string s) boolean -> s.len <= 8
log check("osl", [notEmpty, short])
log check("much too long", [notEmpty, short])Async lambdas#
A lambda can run on its own thread too. Write async where def would go. Calling it returns a thread handle, and .wait() gives the value:
auto double = async(int n) int -> n * 2
auto shout = async(string s) string (
thread.sleep(10)
return s.toUpper()
)
log double(4).wait()
log shout("hi").wait()The concurrency lesson shows them passed around and run several at a time.
Pipes#
|> passes the value on its left to the function on its right, so a chain of steps reads in the order it happens:
def double(int n) int -> n * 2
def addOne(int n) int -> n + 1
log 5 |> double |> addOne
log addOne(double(5))What does this print?
auto make = def(int start) def() int (
int n = start
return def() int -> (
n += 5
return n
)
)
auto f = make(10)
f()
log f()Longest word
Use sortBy with a lambda to print the words from longest to shortest.
["banana","apple","kiwi","fig"]