Functional
Tova provides higher-order utility functions for composition, memoization, and controlling function execution.
Composition
compose
compose(...fns) -> FunctionCreates a new function that applies functions right-to-left. The last function is applied first.
double = fn(x) x * 2
inc = fn(x) x + 1
double_then_inc = compose(inc, double)
double_then_inc(3) // 7 (double(3) = 6, inc(6) = 7)
// Compose many functions
process = compose(upper, trim, fn(s) replace(s, " ", " "))
process(" hello world ") // "HELLO WORLD"pipe_fn
pipe_fn(...fns) -> FunctionCreates a new function that applies functions left-to-right. The first function is applied first. This is the reverse of compose.
double = fn(x) x * 2
inc = fn(x) x + 1
inc_then_double = pipe_fn(inc, double)
inc_then_double(3) // 8 (inc(3) = 4, double(4) = 8)
// Build a text processing pipeline
clean = pipe_fn(trim, lower, fn(s) replace(s, " ", " "))
clean(" Hello World ") // "hello world"identity
identity(x) -> xReturns its argument unchanged. Useful as a default function or placeholder in compositions.
identity(42) // 42
identity("hello") // "hello"
// Useful as a default transformer
fn process(items, transform?) {
t = transform ?? identity
map(items, t)
}negate
negate(fn) -> FunctionReturns a new function that negates the result of the given predicate function.
is_even = fn(x) x % 2 == 0
is_odd = negate(is_even)
is_odd(3) // true
is_odd(4) // false
// Filter with negated predicate
filter([1, 2, 3, 4, 5], negate(fn(x) x > 3))
// [1, 2, 3]Caching & Control
memoize
memoize(fn) -> FunctionReturns a version of the function that caches results based on arguments. Subsequent calls with the same arguments return the cached result without re-executing.
expensive = memoize(fn(n) {
// Simulates expensive computation
range(n) |> map(fn(x) x * x) |> sum()
})
expensive(1000) // computed
expensive(1000) // cached -- instantonce
once(fn) -> FunctionReturns a function that executes only on the first call. Subsequent calls return the first result.
init = once(fn() {
print("Initializing...")
{ ready: true }
})
init() // prints "Initializing...", returns { ready: true }
init() // returns { ready: true } -- no print
init() // returns { ready: true } -- no printTiming
debounce
debounce(fn, ms) -> FunctionReturns a debounced function that delays execution until ms milliseconds have passed since the last call. Useful for search-as-you-type or resize handlers.
search = debounce(fn(query) {
print("Searching: {query}")
}, 300)
// Only the last call within 300ms fires
search("h")
search("he")
search("hel")
search("hello")
// After 300ms: "Searching: hello"throttle
throttle(fn, ms) -> FunctionReturns a throttled function that executes at most once every ms milliseconds. Useful for scroll handlers or rate-limiting.
log_scroll = throttle(fn(pos) {
print("Scroll: {pos}")
}, 100)
// No matter how often called, fires at most every 100msPartial Application & Currying
partial
partial(fn, ...args) -> FunctionReturns a new function with some arguments pre-filled. The remaining arguments are supplied when the returned function is called.
add = fn(a, b) a + b
add5 = partial(add, 5)
add5(3) // 8
add5(10) // 15
// Pre-fill multiple arguments
greet = fn(greeting, name) "{greeting}, {name}!"
hello = partial(greet, "Hello")
hello("Alice") // "Hello, Alice!"curry
curry(fn, arity?) -> FunctionReturns a curried version of the function. Each argument can be supplied one at a time, and the function executes when all arguments are provided.
add = curry(fn(a, b, c) a + b + c)
add(1)(2)(3) // 6
add(1, 2)(3) // 6
add(1)(2, 3) // 6
add(1, 2, 3) // 6flip
flip(fn) -> FunctionReturns a new function with the first two arguments swapped.
sub = fn(a, b) a - b
flipped_sub = flip(sub)
sub(10, 3) // 7
flipped_sub(3, 10) // 7 (calls sub(10, 3))
// Useful for making functions pipe-friendly
contains_in = flip(contains)
"hello" |> contains_in("ell") // truePipeline Examples
// Build a reusable data pipeline
process_users = pipe_fn(
fn(users) filter(users, fn(u) u.active),
fn(users) sorted(users, fn(u) u.name),
fn(users) map(users, fn(u) pick(u, ["name", "email"]))
)
process_users(all_users)
// Compose validators
validate = compose(
fn(s) if len(s) < 3 { Err("too short") } else { Ok(s) },
fn(s) trim(s)
)
// Partial application for reusable transforms
multiply = curry(fn(a, b) a * b)
double = multiply(2)
triple = multiply(3)
[1, 2, 3] |> map(double) // [2, 4, 6]
[1, 2, 3] |> map(triple) // [3, 6, 9]