Blocks
Although Agency doesn't have lambdas the way JavaScript does, it has a similar feature called blocks. You can use this to define functions that take another function. For example, let's use Agency's built-in map function, which takes a block:
Using blocks
One parameter
const names: string[] = ["Alice", "Bob", "Charlie"]
const greetings = map(names) as name {
return "Hi, ${name}!"
}You bind the parameter using as.
No parameters
With no params, you don't need as:
def callMe(block) {
return block()
}
node main() {
const foo = callMe() {
return 5
}
// prints 5
print(foo)
}But you still need the parentheses after callMe. This won't parse:
const foo = callMe {
return 5
}Multiple parameters
const names: string[] = ["Alice", "Bob", "Charlie"]
const greetings = mapWithIndex(names) as (name, index) {
return "${index}: ${name}"
}Writing functions that take blocks
def callMe(block: (any) -> any) {
return block()
}Inline blocks
For simple one-liner blocks, you can use the inline block syntax. Instead of writing the block after the function call, you write it as an argument using \:
const names: string[] = ["Alice", "Bob", "Charlie"]
const greetings = map(names, \name -> "Hi, ${name}!")For multiple parameters, wrap them in parentheses:
const greetings = mapWithIndex(names, \(name, index) -> "${index}: ${name}")For no parameters:
const results = twice(\ -> "hello")Inline blocks are single-line only, and the expression is implicitly returned.
Limitations of blocks
Don't assign the block to another variable
def foo(block: () => any) {
let saved = block
doSomething()
let result = saved()
}[Side note] Why this happens: The issue is that when Agency resumes from an interrupt, it replays execution from the beginning of each function, skipping past already-completed steps. The step that copied
blocktosavedalready completed, so it's skipped. The deserializedsavedhas lost its connection to the enclosing scope. This works fine if the block doesn't throw an interrupt.
Just use the block parameter directly instead of copying it:
def foo(block: () => any) {
doSomething()
// uses the parameter directly — this is fine
let result = block()
}Don't return the block from a function
def foo(block: () => any) {
// don't do this
return block
}Blocks and functions
You can use a function or a PFA anywhere you can pass in a block:
def add(a: number, b: number): number {
return a + b
}
const result = map(range(10), add.partial(b: 5))