How do I use BiFunction and BiRoutine for two-input transformations?
← Generics · Ref: Q713
BiFunction and BiRoutine are the three-type-parameter transformation types. They take inputs 't' of type T and 'u' of type U, and return 'r' of type R.
BIFUNCTION IS PURE
BiFunction of (T, U, R) takes two parameters and returns a result. Being pure, the transformation is deterministic:
combiner <- () is BiFunction of (String, Integer, String) as pure function r: `${t}: ${u}`
BIROUTINE IS IMPURE
BiRoutine of (T, U, R) has the same signature but is NOT pure:
logCombiner <- () is BiRoutine of (String, Integer, String) as function stdout <- Stdout() stdout.println(`Combining: ${t} with ${u}`) r: `${t}-${u}`
COMBINER PATTERN
BiFunction is ideal for combining two values into one:
merge() as pure -> a as String, b as Integer, combiner as BiFunction of (String, Integer, String) <- result as String: combiner(a, b)
See Q54 for pure vs impure concepts. See Q710 for single-input Function/Routine.
Example
defines module qa.genericsdeep.bifunctionbiroutine defines function <?- Helper that applies a pure BiFunction combiner. -?> merge() as pure -> name as String count as Integer combiner as BiFunction of (String, Integer, String) <- result as String: combiner(name, count) <?- Helper that applies an impure BiRoutine combiner. -?> mergeWithEffects() -> name as String count as Integer combiner as BiRoutine of (String, Integer, String) <- result as String: combiner(name, count) defines program BiFunctionBiRoutineDemo() stdout <- Stdout() //BiFunction: pure, deterministic two-input transform //Body uses 't' and 'u' for inputs, 'r' for return combiner <- () is BiFunction of (String, Integer, String) as pure function r: `${t}: ${u}` stdout.println(`Combined: ${merge("Alice", 42, combiner)}`) //BiFunction with Boolean result checker <- () is BiFunction of (String, Integer, Boolean) as pure function r: length t > u stdout.println(`Length check: ${checker("Hello", 3)}`) //BiRoutine: impure, can log during transformation logCombiner <- () is BiRoutine of (String, Integer, String) as function stdout <- Stdout() stdout.println(`Combining: ${t} with ${u}`) r: `${t}-${u}` result <- mergeWithEffects("Bob", 99, logCombiner) stdout.println(`BiRoutine result: ${result}`)
Common mistakes
E05150 — BiFunction is pure — dynamic functions extending BiFunction must use 'as pure function', not just 'as function'. The compiler requires the purity of the implementation to match its pure super type. See ek9 -h E05150 for details.
Incorrect:
() is BiFunction of (String, Integer, String) as function
Correct:
() is BiFunction of (String, Integer, String) as pure function
Other ways to ask this
- What is the difference between BiFunction and BiRoutine?
- How do I create a BiFunction of (String, Integer, Boolean)?
- When should I use BiRoutine instead of BiFunction?
Coming from another language?
Java: java.util.function.BiFunction<T,U,R> — no purity distinction, supports andThen composition. Kotlin: (T, U) -> R — no pure/impure variants. Rust: Fn(T, U) -> R (pure) vs FnMut(T, U) -> R (stateful). Go: func(T, U) R — no purity. C#: Func<T1, T2, TResult> — no purity concept. EK9: BiFunction (pure, deterministic) vs BiRoutine (impure, can access state) — compile-time enforced.
Keywords: three-parameter, pure, function-type, biroutine, combine, side-effect, bifunction, transform, generic, two-input, impure