Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Higher-Order Functions

Higher-order functions accept a list and a lambda (anonymous function) and apply the lambda to elements. They are the primary tool for transforming and testing collections in WCL.

Lambdas use the => syntax:

// Single parameter — no parentheses needed
x => x * 2

// Multiple parameters
(acc, x) => acc + x

Lambdas can also reference named functions from let bindings:

let double = x => x * 2
let doubled = map([1, 2, 3], double)   // [2, 4, 6]

Reference

FunctionSignatureDescription
mapmap(list: list, fn: lambda) -> listApply fn to each element; return list of results
filterfilter(list: list, fn: lambda) -> listKeep elements for which fn returns true
everyevery(list: list, fn: lambda) -> boolTrue if fn returns true for all elements
somesome(list: list, fn: lambda) -> boolTrue if fn returns true for at least one element
reducereduce(list: list, fn: lambda, initial) -> anyFold list into a single value using fn(accumulator, element)

map

Transform each element of a list:

let nums = [1, 2, 3, 4, 5]
let squared = map(nums, x => x * x)       // [1, 4, 9, 16, 25]
let strs = map(nums, x => to_string(x))   // ["1", "2", "3", "4", "5"]

Map over strings extracted from blocks:

let services = query(service)
let names = map(services, s => s.name)    // ["web", "api", "worker"]

filter

Keep only elements that satisfy a predicate:

let nums = [1, 2, 3, 4, 5, 6]
let evens = filter(nums, x => x % 2 == 0)   // [2, 4, 6]
let large = filter(nums, x => x > 3)        // [4, 5, 6]

Filter blocks by attribute values:

let prod_svcs = filter(query(service), s => s.env == "prod")

every

Check that all elements satisfy a predicate:

let all_positive = every([1, 2, 3], x => x > 0)     // true
let all_even     = every([2, 4, 6], x => x % 2 == 0) // true
let all_small    = every([1, 2, 100], x => x < 10)   // false

Short-circuits on the first false result.

some

Check that at least one element satisfies a predicate:

let has_large = some([1, 2, 3, 100], x => x > 50)   // true
let has_neg   = some([1, 2, 3], x => x < 0)         // false

Short-circuits on the first true result.

reduce

Fold a list into a single accumulated value:

let nums = [1, 2, 3, 4, 5]
let total = reduce(nums, (acc, x) => acc + x, 0)    // 15
let product = reduce(nums, (acc, x) => acc * x, 1)  // 120

Build a string from a list:

let words = ["hello", "world", "wcl"]
let sentence = reduce(words, (acc, w) => acc + " " + w, "")
// " hello world wcl"  (note the leading space from the empty initial)

Build a map from a list of pairs:

let pairs = [["a", 1], ["b", 2], ["c", 3]]
let m = reduce(pairs, (acc, p) => merge(acc, {[p[0]]: p[1]}), {})
// {a: 1, b: 2, c: 3}

Composing Higher-Order Functions

Higher-order functions compose naturally:

let nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

// Sum of squares of even numbers
let result = reduce(
  map(filter(nums, x => x % 2 == 0), x => x * x),
  (acc, x) => acc + x,
  0
)
// filter -> [2, 4, 6, 8, 10]
// map    -> [4, 16, 36, 64, 100]
// reduce -> 220

Using Named Lambdas

Store predicates and transformers in let bindings for reuse:

let is_prod = s => s.env == "prod"
let get_port = s => s.port

let prod_services = filter(query(service), is_prod)
let prod_ports    = map(prod_services, get_port)

Exported lambdas (via export let) can be shared across files the same way as data.