Velo by Example: Higher-Order Functions

Functions in Velo are first-class values: pass them as arguments, return them, store them in variables. Use func[T] as the parameter type when only the return type matters.

A function that takes another function as a parameter. func[int] means "any callable that returns int".

func apply(int x, func[int] f) int {
    f(x);
};

any square = func(int x) int {
    x * x;
};

int result = apply(5, square);  # 25

The built-in map method on arrays is a higher-order function. It takes a callback with (index, value) parameters.

array[int] nums = new array[int]{1, 2, 3, 4, 5};

array[int] doubled = nums.map(
    func(int i, int v) int {
        v * 2
    }
);  # [2, 4, 6, 8, 10]

twice applies a function to its own result.

func twice(int x, func[int] f) int {
    f(f(x));
};

any inc = func(int x) int { x + 1; };
int y = twice(3, inc);  # 5

Compose two functions to build a new one. The result is itself a func[int] value that can be stored, passed around, or invoked later.

func compose(func[int] f, func[int] g) func[int] {
    func(int x) int {
        f(g(x));
    };
};

func[int] incThenSquare = compose(square, inc);
int r = incThenSquare(4);  # (4+1)^2 = 25

A predicate-driven counter. The lambda is built at the call site and captures threshold from the surrounding scope.

func count(array[int] arr, func[bool] pred) int {
    int n = 0;
    int i = 0;
    while (i < arr.len) {
        if (pred(arr[i])) {
            n = n + 1;
        };
        i = i + 1;
    };
    n;
};

int threshold = 10;
int big = count(
    new array[int]{3, 12, 7, 25},
    func(int v) bool { v > threshold; }
);  # 2