Skip to main content

Lambdas and Higher-Order Functions

Process lists declaratively using lambda expressions and higher-order functions: filter, map, all, any.

Use Case

You have a list of numbers and need to filter, transform, and validate them without writing explicit loops.

The Pipeline

# lambdas-and-hof.cst

use stdlib.collection

in numbers: List<Int>

# Filter: keep elements matching predicate
positives = numbers filter it > 0
above10 = numbers filter it > 10

# Map: transform each element
doubled = numbers map it * 2
plus10 = numbers map it + 10

# All: check if all elements satisfy predicate
allPositive = numbers all it > 0
allBelow100 = numbers all it < 100

# Any: check if any element satisfies predicate
hasNegative = numbers any it < 0
hasAbove100 = numbers any it > 100

# Chaining: filter then transform (left-to-right)
positivesDoubled = numbers filter it > 0 map it * 2

out positives
out above10
out doubled
out plus10
out allPositive
out allBelow100
out hasNegative
out hasAbove100
out positivesDoubled

Explanation

FunctionSignatureReturnsPurpose
filterfilter(list, predicate)List<T>Keep elements where predicate is true
mapmap(list, transform)List<U>Apply transform to each element
allall(list, predicate)BooleanTrue if predicate holds for all elements
anyany(list, predicate)BooleanTrue if predicate holds for at least one element

Three equivalent forms

All higher-order functions support three calling styles:

# Infix + implicit it (most concise)
positives = numbers filter it > 0

# Prefix + implicit it
positives = filter(numbers, it > 0)

# Explicit lambda (most explicit)
positives = filter(numbers, (x) => x > 0)

Use infix for simple predicates and transforms. Use explicit lambdas when the body is complex or when naming the parameter aids clarity.

tip

Lambda bodies support closure capture — you can reference variables from the enclosing scope:

in threshold: Int
above = numbers filter it > threshold # captures `threshold`

Running the Example

Input

{
"numbers": [5, -3, 15, 8, -1, 42]
}

Expected Output

{
"positives": [5, 15, 8, 42],
"above10": [15, 42],
"doubled": [10, -6, 30, 16, -2, 84],
"plus10": [15, 7, 25, 18, 9, 52],
"allPositive": false,
"allBelow100": true,
"hasNegative": true,
"hasAbove100": false,
"positivesDoubled": [10, 30, 16, 84]
}

Variations

Filter then check

use stdlib.collection

in scores: List<Int>

passing = scores filter it >= 60
allPassing = scores all it >= 60

out passing
out allPassing

Chaining pipelines

use stdlib.collection

in numbers: List<Int>

# Left-to-right pipeline: filter, then transform
result = numbers filter it > 0 map it * 10

# Equivalent nested form
result2 = map(filter(numbers, it > 0), it * 10)

out result
tip

Filter before map. Running numbers filter pred map transform is more efficient than mapping first and filtering after, because fewer elements are transformed.

Best Practices

  1. Use infix for readabilitynumbers filter it > 0 reads naturally left-to-right
  2. Chain operations — filter first, then map, to avoid transforming elements you'll discard
  3. Use all/any for validation — check batch constraints without manual iteration
  4. Capture outer variables — closures work with all three calling forms