Functional Composition and Pipelines
Compose functions and build transformation pipelines with Kotlin higher-order functions.
// Function composition
infix fun <A, B, C> ((B) -> C).compose(f: (A) -> B): (A) -> C = { a -> this(f(a)) }
infix fun <A, B, C> ((A) -> B).andThen(f: (B) -> C): (A) -> C = { a -> f(this(a)) }
// Pipeline operator (like Unix pipe)
infix fun <A, B> A.pipe(f: (A) -> B): B = f(this)
// Currying
fun <A, B, C> ((A, B) -> C).curry(): (A) -> (B) -> C = { a -> { b -> this(a, b) } }
// Memoize
fun <A, R> ((A) -> R).memoize(): (A) -> R {
val cache = mutableMapOf<A, R>()
return { a -> cache.getOrPut(a) { this(a) } }
}
// Builder-style pipeline
class Pipeline<T>(private val value: T) {
fun <R> map(transform: (T) -> R) = Pipeline(transform(value))
fun <R> flatMap(transform: (T) -> Pipeline<R>) = transform(value)
fun filter(predicate: (T) -> Boolean): Pipeline<T?> =
Pipeline(if (predicate(value)) value else null)
fun get() = value
}
fun <T> pipeline(value: T) = Pipeline(value)
fun main() {
// Compose functions
val double = { x: Int -> x * 2 }
val addOne = { x: Int -> x + 1 }
val square = { x: Int -> x * x }
val doubleThenAdd = addOne compose double // addOne(double(x))
val addThenDouble = double compose addOne // double(addOne(x))
println("doubleThenAdd(5): ${doubleThenAdd(5)}") // 11
println("addThenDouble(5): ${addThenDouble(5)}") // 12
// andThen (left to right)
val transform = double andThen addOne andThen square
println("transform(3): ${transform(3)}") // (3*2+1)^2 = 49
// Pipe operator
val result = 5 pipe double pipe addOne pipe square
println("Piped: $result") // 121
// String processing pipeline
val cleanText = { s: String -> s.trim() }
val lowercase = { s: String -> s.lowercase() }
val removeSpaces = { s: String -> s.replace(" ", "-") }
val slugify = cleanText andThen lowercase andThen removeSpaces
println("Slug: ${slugify(" Hello World ")}") // hello-world
// Currying
val add = { a: Int, b: Int -> a + b }
val curriedAdd = add.curry()
val add5 = curriedAdd(5)
println("Curried add5(3): ${add5(3)}") // 8
// Memoized fibonacci
lateinit var fib: (Int) -> Long
fib = { n: Int ->
if (n <= 1) n.toLong() else fib(n - 1) + fib(n - 2)
}.memoize()
println("Fib(40): ${fib(40)}")
// Pipeline builder
val output = pipeline(" Hello World ")
.map { it.trim() }
.map { it.lowercase() }
.map { it.replace(" ", "_") }
.map { it.uppercase() }
.get()
println("Pipeline: $output") // HELLO_WORLD
}Use Cases
- Data transformation pipelines
- Reusable function composition
- Text processing and slug generation
Tags
Related Snippets
Similar patterns you can reuse in the same workflow.
Collections — map, filter, groupBy, and More
Master Kotlin collections: functional transformations, aggregations, grouping, and partition operations.
Best for: Data processing and transformation pipelines
Result Monad — Functional Error Handling
Handle errors functionally with Kotlin Result: map, recover, fold, and chaining fallible operations.
Best for: Type-safe error handling without exceptions
Higher-Order Functions and Lambda Patterns
Compose behavior with higher-order functions: function types, lambda receivers, and functional composition.
Best for: Composable transformation pipelines
Functional Error Handling with Either
Use Either and Option for type-safe error handling: Railway-oriented programming without exceptions.
Best for: Type-safe error handling without exceptions