Functional companion to Kotlin's Standard Library providing core data types and error handling
—
Comprehensive utilities for function composition, transformation, and functional programming patterns. Provides building blocks for higher-order functional programming including composition, currying, memoization, and more.
Fundamental functional programming utilities.
/**
* Identity function - returns its argument unchanged
*/
fun <A> identity(a: A): A
/**
* Constant function - returns a function that always returns the given value
*/
fun <A, B> const(value: A): (B) -> AUsage Examples:
val numbers = listOf(1, 2, 3, 4, 5)
// Identity is useful for default transformations
val unchanged = numbers.map(::identity) // [1, 2, 3, 4, 5]
// Constant functions
val alwaysTrue: (String) -> Boolean = const(true)
val result = listOf("a", "b", "c").map(alwaysTrue) // [true, true, true]Combine functions to create new functions.
/**
* Right-to-left function composition
* (g ∘ f)(x) = g(f(x))
*/
fun <A, B, C> ((B) -> C).compose(f: (A) -> B): (A) -> C
/**
* Left-to-right function composition
* (f andThen g)(x) = g(f(x))
*/
fun <A, B, C> ((A) -> B).andThen(g: (B) -> C): (A) -> CUsage Examples:
val addOne: (Int) -> Int = { it + 1 }
val multiplyByTwo: (Int) -> Int = { it * 2 }
val toString: (Int) -> String = { it.toString() }
// Right-to-left composition
val addThenMultiply = multiplyByTwo.compose(addOne)
val result1 = addThenMultiply(5) // multiplyByTwo(addOne(5)) = multiplyByTwo(6) = 12
// Left-to-right composition
val multiplyThenString = multiplyByTwo.andThen(toString)
val result2 = multiplyThenString(5) // toString(multiplyByTwo(5)) = toString(10) = "10"
// Chain multiple compositions
val pipeline = addOne
.andThen(multiplyByTwo)
.andThen(toString)
.andThen { "Result: $it" }
val finalResult = pipeline(5) // "Result: 12"Transform multi-parameter functions into sequences of single-parameter functions.
/**
* Curry a 2-parameter function
*/
fun <A, B, C> ((A, B) -> C).curried(): (A) -> (B) -> C
/**
* Uncurry a curried 2-parameter function
*/
fun <A, B, C> ((A) -> (B) -> C).uncurried(): (A, B) -> C
/**
* Curry a 3-parameter function
*/
fun <A, B, C, D> ((A, B, C) -> D).curried(): (A) -> (B) -> (C) -> D
/**
* Partial application - fix the first parameter
*/
fun <A, B, C> ((A, B) -> C).partially1(a: A): (B) -> C
/**
* Partial application - fix the second parameter
*/
fun <A, B, C> ((A, B) -> C).partially2(b: B): (A) -> CUsage Examples:
val add: (Int, Int) -> Int = { a, b -> a + b }
val multiply: (Int, Int, Int) -> Int = { a, b, c -> a * b * c }
// Currying
val curriedAdd = add.curried()
val addFive = curriedAdd(5) // (Int) -> Int
val result = addFive(3) // 8
// Partial application
val add10 = add.partially1(10)
val result2 = add10(5) // 15
// Currying with multiple parameters
val curriedMultiply = multiply.curried()
val multiplyBy2 = curriedMultiply(2)
val multiplyBy2And3 = multiplyBy2(3)
val finalResult = multiplyBy2And3(4) // 2 * 3 * 4 = 24
// Practical example: creating specialized validators
val validate: (String, Int) -> Boolean = { str, minLength -> str.length >= minLength }
val validateEmail = validate.partially2(5) // Minimum 5 characters
val isValidEmail = validateEmail("user@example.com") // trueCache function results to avoid recomputation.
/**
* Memoize a single-parameter function
*/
fun <A, B> ((A) -> B).memoize(): (A) -> B
/**
* Memoize a two-parameter function
*/
fun <A, B, C> ((A, B) -> C).memoize(): (A, B) -> CUsage Examples:
// Expensive computation
fun fibonacci(n: Int): Long = when {
n <= 1 -> n.toLong()
else -> fibonacci(n - 1) + fibonacci(n - 2)
}
// Memoized version for performance
val memoizedFib = ::fibonacci.memoize()
// First call computes and caches
val result1 = memoizedFib(40) // Takes time to compute
// Second call returns cached result instantly
val result2 = memoizedFib(40) // Returns immediately
// Practical example: expensive API calls
val fetchUserData: (String) -> UserData = { userId ->
// Expensive network call
apiClient.getUser(userId)
}
val cachedFetchUser = fetchUserData.memoize()
// Multiple calls with same ID use cached result
val user1 = cachedFetchUser("user123") // Network call
val user2 = cachedFetchUser("user123") // Cached resultAdditional utilities for working with functions.
/**
* Create a function that ignores its input and returns Unit
*/
fun <A> unit(): (A) -> Unit
/**
* Flip the order of parameters in a 2-parameter function
*/
fun <A, B, C> ((A, B) -> C).flip(): (B, A) -> C
/**
* Convert a function to accept a Pair instead of two parameters
*/
fun <A, B, C> ((A, B) -> C).tupled(): (Pair<A, B>) -> C
/**
* Convert a function that accepts a Pair to accept two parameters
*/
fun <A, B, C> ((Pair<A, B>) -> C).untupled(): (A, B) -> CUsage Examples:
val divide: (Int, Int) -> Double = { a, b -> a.toDouble() / b }
val processUser: (String) -> Unit = { name -> println("Processing $name") }
// Flip parameter order
val divideFlipped = divide.flip()
val result1 = divide(10, 2) // 5.0 (10 / 2)
val result2 = divideFlipped(10, 2) // 0.2 (2 / 10)
// Tuple/untuple conversion
val tupledDivide = divide.tupled()
val result3 = tupledDivide(Pair(15, 3)) // 5.0
// Unit function for side effects
val logAndIgnore = unit<String>()
listOf("a", "b", "c").map(logAndIgnore) // [Unit, Unit, Unit]Stack-safe lazy evaluation for recursive computations.
/**
* Lazy evaluation container
*/
sealed class Eval<out A> {
abstract fun value(): A
companion object {
/**
* Eager evaluation - value computed immediately
*/
fun <A> now(value: A): Eval<A>
/**
* Lazy evaluation - value computed once when needed
*/
fun <A> later(f: () -> A): Eval<A>
/**
* Lazy evaluation - value computed every time when needed
*/
fun <A> always(f: () -> A): Eval<A>
}
}
/**
* Transform the lazy value
*/
fun <A, B> Eval<A>.map(f: (A) -> B): Eval<B>
/**
* Stack-safe monadic bind
*/
fun <A, B> Eval<A>.flatMap(f: (A) -> Eval<B>): Eval<B>
/**
* Memoize the computation (convert to 'later' behavior)
*/
fun <A> Eval<A>.memoize(): Eval<A>Usage Examples:
// Stack-safe recursive fibonacci
fun fibEval(n: Int): Eval<Long> = when {
n <= 1 -> Eval.now(n.toLong())
else -> fibEval(n - 1).flatMap { a ->
fibEval(n - 2).map { b -> a + b }
}
}
// Safe computation of large fibonacci numbers
val largeFib = fibEval(1000).value() // Won't stack overflow
// Lazy expensive computation
val expensiveComputation = Eval.later {
println("Computing...")
Thread.sleep(1000)
42
}
// Value not computed until needed
println("Created lazy computation")
val result = expensiveComputation.value() // "Computing..." printed here
// Memoization
val memoized = expensiveComputation.memoize()
val result1 = memoized.value() // Computes once
val result2 = memoized.value() // Uses cached resultUtilities for stack-safe recursive functions.
/**
* Create a memoized deep recursive function
*/
fun <T, R> memoizedDeepRecursiveFunction(
calculation: DeepRecursiveScope<T, R>.(T) -> R
): (T) -> RUsage Examples:
// Stack-safe memoized recursive function
val factorialMemo = memoizedDeepRecursiveFunction<Int, Long> { n ->
when {
n <= 1 -> 1L
else -> n * callRecursive(n - 1)
}
}
val largeFactorial = factorialMemo(1000) // Safe for large inputs
// Tree traversal example
data class Tree<A>(val value: A, val children: List<Tree<A>> = emptyList())
val sumTree = memoizedDeepRecursiveFunction<Tree<Int>, Long> { tree ->
tree.value.toLong() + tree.children.sumOf { callRecursive(it) }
}Create complex data processing pipelines.
val dataProcessor = { input: String ->
input.trim()
}
.andThen { it.lowercase() }
.andThen { it.split(" ") }
.andThen { words -> words.filter { it.isNotEmpty() } }
.andThen { it.joinToString("-") }
val result = dataProcessor(" Hello World ") // "hello-world"Apply functions conditionally.
fun <A> ((A) -> A).applyIf(condition: Boolean): (A) -> A =
if (condition) this else ::identity
val processor = { text: String -> text.uppercase() }
.applyIf(shouldCapitalize)
.andThen { it.trim() }
.applyIf(shouldClean)
val result = processor(input)Install with Tessl CLI
npx tessl i tessl/maven-io-arrow-kt--arrow-core