CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-io-arrow-kt--arrow-core

Functional companion to Kotlin's Standard Library providing core data types and error handling

Pending
Overview
Eval results
Files

functional-utilities.mddocs/

Functional Utilities

Comprehensive utilities for function composition, transformation, and functional programming patterns. Provides building blocks for higher-order functional programming including composition, currying, memoization, and more.

Capabilities

Core Functions

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) -> A

Usage 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]

Function Composition

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) -> C

Usage 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"

Currying

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) -> C

Usage 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")  // true

Memoization

Cache 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) -> C

Usage 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 result

Function Utilities

Additional 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) -> C

Usage 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]

Lazy Evaluation with Eval

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 result

Recursive Function Utilities

Utilities for stack-safe recursive functions.

/**
 * Create a memoized deep recursive function
 */
fun <T, R> memoizedDeepRecursiveFunction(
    calculation: DeepRecursiveScope<T, R>.(T) -> R
): (T) -> R

Usage 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) }
}

Advanced Patterns

Function Pipeline

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"

Conditional Composition

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

docs

collections.md

error-handling.md

functional-utilities.md

inclusive-or.md

index.md

optional-values.md

raise-dsl.md

tile.json