Lambdas
Some built-in parameters are functions: the predicate of filter, the transform of map, the key of sortBy, and so on. The argument for such a parameter is a lambda. Lambdas exist only as arguments: they cannot be stored in a variable, put in a list, or returned.
Arrow lambdas
Arrow lambdas look like JavaScript arrow functions with an expression body. Lambdas for list functions receive the item and its index.
users.filter(u => u.age >= 18).map(u => u.name) // => ["Ada", "Cy"]
users.map((u, i) => `${i + 1}. ${u.name}`) // => ["1. Ada", "2. Bo", "3. Cy"]
users.reduce((total, u) => total + u.score, 0) // => 40The implicit . lambda
. is shorthand for the current item. An argument that uses a free . becomes a one-parameter lambda, so .age >= 18 means u => u.age >= 18:
users.filter(.age >= 18 && !.suspended).map(.name) // => ["Ada"]
users.map({ name: .name, adult: .age >= 18 }).first() // => { name: "Ada", adult: true }
users.sortBy(.age, "desc").map(.name) // => ["Cy", "Ada", "Bo"]
xs.filter(10 < .) // => [12, 20]
xs.map(. * 2) // => [10, 24, 40]
rows.map(.[0]) // => [1, 3]. is only allowed where the lambda's first parameter is the current item. In reduce the first parameter is the accumulator, so . there is a check error; name both parameters instead:
xs.reduce(. + 10, 0) // error: INVALID_LAMBDA
xs.reduce((total, x) => total + x, 0) // => 37How . binds
. binds to the nearest enclosing argument whose parameter is a function, skipping arguments of ordinary parameters. The binding is decided by the declared parameter types, not by syntax, which is what makes these work:
users.filter(.score > max(.bonus, 10)).map(.name) // => ["Ada"]
groups.map(.users.filter(.active).length) // => [1, 1]
groups.map(filter(.users, .active).length) // => [1, 1]- In the first line,
maxtakes ordinary numbers, so.bonusstill belongs to thefilterlambda: each user's score is compared with the larger of their bonus and 10. - In the second,
.usersis the group (themaplambda) and.activeis a user (the innerfilterlambda). - The third is the same call in prefix form.
Naming the outer item
To use an outer item inside an inner lambda, name it with an arrow lambda. Inside the arrow lambda's body, . may still be used in a nested function argument:
orders.filter(o => o.lines.some(.sku == o.promoSku)).map(.id) // => ["A1"]Rules
.directly inside an explicit lambda body is an error: use the parameter instead..outside any function argument is an error.- A lambda anywhere other than a function argument is a syntax error.
- A lambda whose parameter must return a boolean (as in
filter) may returnnull, which counts asfalse, so a boolean field missing from some items filters them out. Any other non-boolean result is a type error: test whether a field is present with!= null, not by returning the field itself.
.age // error: INVALID_LAMBDA
users.map(u => .age) // error: INVALID_LAMBDA
[x => x] // error: SYNTAX
users.filter(.vip).length // => 0
users.filter(.nickname != null).length // => 0
users.filter(.age) // error: TYPE_ERROREvaluation
Higher-order functions call their lambda sequentially, in list order, and stop as soon as the result is known: some stops at the first true, every and none at the first counterexample, find and findIndex at the first match. Each lambda call counts against the step budget.
The functions that take lambdas are map, filter, find, findIndex, some, every, none, count, flatMap, reduce, sortBy, and groupBy. Host functions do not take lambdas.