0
# Functional Utilities
1
2
Comprehensive utilities for function composition, transformation, and functional programming patterns. Provides building blocks for higher-order functional programming including composition, currying, memoization, and more.
3
4
## Capabilities
5
6
### Core Functions
7
8
Fundamental functional programming utilities.
9
10
```kotlin { .api }
11
/**
12
* Identity function - returns its argument unchanged
13
*/
14
fun <A> identity(a: A): A
15
16
/**
17
* Constant function - returns a function that always returns the given value
18
*/
19
fun <A, B> const(value: A): (B) -> A
20
```
21
22
**Usage Examples:**
23
24
```kotlin
25
val numbers = listOf(1, 2, 3, 4, 5)
26
27
// Identity is useful for default transformations
28
val unchanged = numbers.map(::identity) // [1, 2, 3, 4, 5]
29
30
// Constant functions
31
val alwaysTrue: (String) -> Boolean = const(true)
32
val result = listOf("a", "b", "c").map(alwaysTrue) // [true, true, true]
33
```
34
35
### Function Composition
36
37
Combine functions to create new functions.
38
39
```kotlin { .api }
40
/**
41
* Right-to-left function composition
42
* (g ∘ f)(x) = g(f(x))
43
*/
44
fun <A, B, C> ((B) -> C).compose(f: (A) -> B): (A) -> C
45
46
/**
47
* Left-to-right function composition
48
* (f andThen g)(x) = g(f(x))
49
*/
50
fun <A, B, C> ((A) -> B).andThen(g: (B) -> C): (A) -> C
51
```
52
53
**Usage Examples:**
54
55
```kotlin
56
val addOne: (Int) -> Int = { it + 1 }
57
val multiplyByTwo: (Int) -> Int = { it * 2 }
58
val toString: (Int) -> String = { it.toString() }
59
60
// Right-to-left composition
61
val addThenMultiply = multiplyByTwo.compose(addOne)
62
val result1 = addThenMultiply(5) // multiplyByTwo(addOne(5)) = multiplyByTwo(6) = 12
63
64
// Left-to-right composition
65
val multiplyThenString = multiplyByTwo.andThen(toString)
66
val result2 = multiplyThenString(5) // toString(multiplyByTwo(5)) = toString(10) = "10"
67
68
// Chain multiple compositions
69
val pipeline = addOne
70
.andThen(multiplyByTwo)
71
.andThen(toString)
72
.andThen { "Result: $it" }
73
74
val finalResult = pipeline(5) // "Result: 12"
75
```
76
77
### Currying
78
79
Transform multi-parameter functions into sequences of single-parameter functions.
80
81
```kotlin { .api }
82
/**
83
* Curry a 2-parameter function
84
*/
85
fun <A, B, C> ((A, B) -> C).curried(): (A) -> (B) -> C
86
87
/**
88
* Uncurry a curried 2-parameter function
89
*/
90
fun <A, B, C> ((A) -> (B) -> C).uncurried(): (A, B) -> C
91
92
/**
93
* Curry a 3-parameter function
94
*/
95
fun <A, B, C, D> ((A, B, C) -> D).curried(): (A) -> (B) -> (C) -> D
96
97
/**
98
* Partial application - fix the first parameter
99
*/
100
fun <A, B, C> ((A, B) -> C).partially1(a: A): (B) -> C
101
102
/**
103
* Partial application - fix the second parameter
104
*/
105
fun <A, B, C> ((A, B) -> C).partially2(b: B): (A) -> C
106
```
107
108
**Usage Examples:**
109
110
```kotlin
111
val add: (Int, Int) -> Int = { a, b -> a + b }
112
val multiply: (Int, Int, Int) -> Int = { a, b, c -> a * b * c }
113
114
// Currying
115
val curriedAdd = add.curried()
116
val addFive = curriedAdd(5) // (Int) -> Int
117
val result = addFive(3) // 8
118
119
// Partial application
120
val add10 = add.partially1(10)
121
val result2 = add10(5) // 15
122
123
// Currying with multiple parameters
124
val curriedMultiply = multiply.curried()
125
val multiplyBy2 = curriedMultiply(2)
126
val multiplyBy2And3 = multiplyBy2(3)
127
val finalResult = multiplyBy2And3(4) // 2 * 3 * 4 = 24
128
129
// Practical example: creating specialized validators
130
val validate: (String, Int) -> Boolean = { str, minLength -> str.length >= minLength }
131
val validateEmail = validate.partially2(5) // Minimum 5 characters
132
val isValidEmail = validateEmail("user@example.com") // true
133
```
134
135
### Memoization
136
137
Cache function results to avoid recomputation.
138
139
```kotlin { .api }
140
/**
141
* Memoize a single-parameter function
142
*/
143
fun <A, B> ((A) -> B).memoize(): (A) -> B
144
145
/**
146
* Memoize a two-parameter function
147
*/
148
fun <A, B, C> ((A, B) -> C).memoize(): (A, B) -> C
149
```
150
151
**Usage Examples:**
152
153
```kotlin
154
// Expensive computation
155
fun fibonacci(n: Int): Long = when {
156
n <= 1 -> n.toLong()
157
else -> fibonacci(n - 1) + fibonacci(n - 2)
158
}
159
160
// Memoized version for performance
161
val memoizedFib = ::fibonacci.memoize()
162
163
// First call computes and caches
164
val result1 = memoizedFib(40) // Takes time to compute
165
166
// Second call returns cached result instantly
167
val result2 = memoizedFib(40) // Returns immediately
168
169
// Practical example: expensive API calls
170
val fetchUserData: (String) -> UserData = { userId ->
171
// Expensive network call
172
apiClient.getUser(userId)
173
}
174
175
val cachedFetchUser = fetchUserData.memoize()
176
177
// Multiple calls with same ID use cached result
178
val user1 = cachedFetchUser("user123") // Network call
179
val user2 = cachedFetchUser("user123") // Cached result
180
```
181
182
### Function Utilities
183
184
Additional utilities for working with functions.
185
186
```kotlin { .api }
187
/**
188
* Create a function that ignores its input and returns Unit
189
*/
190
fun <A> unit(): (A) -> Unit
191
192
/**
193
* Flip the order of parameters in a 2-parameter function
194
*/
195
fun <A, B, C> ((A, B) -> C).flip(): (B, A) -> C
196
197
/**
198
* Convert a function to accept a Pair instead of two parameters
199
*/
200
fun <A, B, C> ((A, B) -> C).tupled(): (Pair<A, B>) -> C
201
202
/**
203
* Convert a function that accepts a Pair to accept two parameters
204
*/
205
fun <A, B, C> ((Pair<A, B>) -> C).untupled(): (A, B) -> C
206
```
207
208
**Usage Examples:**
209
210
```kotlin
211
val divide: (Int, Int) -> Double = { a, b -> a.toDouble() / b }
212
val processUser: (String) -> Unit = { name -> println("Processing $name") }
213
214
// Flip parameter order
215
val divideFlipped = divide.flip()
216
val result1 = divide(10, 2) // 5.0 (10 / 2)
217
val result2 = divideFlipped(10, 2) // 0.2 (2 / 10)
218
219
// Tuple/untuple conversion
220
val tupledDivide = divide.tupled()
221
val result3 = tupledDivide(Pair(15, 3)) // 5.0
222
223
// Unit function for side effects
224
val logAndIgnore = unit<String>()
225
listOf("a", "b", "c").map(logAndIgnore) // [Unit, Unit, Unit]
226
```
227
228
### Lazy Evaluation with Eval
229
230
Stack-safe lazy evaluation for recursive computations.
231
232
```kotlin { .api }
233
/**
234
* Lazy evaluation container
235
*/
236
sealed class Eval<out A> {
237
abstract fun value(): A
238
239
companion object {
240
/**
241
* Eager evaluation - value computed immediately
242
*/
243
fun <A> now(value: A): Eval<A>
244
245
/**
246
* Lazy evaluation - value computed once when needed
247
*/
248
fun <A> later(f: () -> A): Eval<A>
249
250
/**
251
* Lazy evaluation - value computed every time when needed
252
*/
253
fun <A> always(f: () -> A): Eval<A>
254
}
255
}
256
257
/**
258
* Transform the lazy value
259
*/
260
fun <A, B> Eval<A>.map(f: (A) -> B): Eval<B>
261
262
/**
263
* Stack-safe monadic bind
264
*/
265
fun <A, B> Eval<A>.flatMap(f: (A) -> Eval<B>): Eval<B>
266
267
/**
268
* Memoize the computation (convert to 'later' behavior)
269
*/
270
fun <A> Eval<A>.memoize(): Eval<A>
271
```
272
273
**Usage Examples:**
274
275
```kotlin
276
// Stack-safe recursive fibonacci
277
fun fibEval(n: Int): Eval<Long> = when {
278
n <= 1 -> Eval.now(n.toLong())
279
else -> fibEval(n - 1).flatMap { a ->
280
fibEval(n - 2).map { b -> a + b }
281
}
282
}
283
284
// Safe computation of large fibonacci numbers
285
val largeFib = fibEval(1000).value() // Won't stack overflow
286
287
// Lazy expensive computation
288
val expensiveComputation = Eval.later {
289
println("Computing...")
290
Thread.sleep(1000)
291
42
292
}
293
294
// Value not computed until needed
295
println("Created lazy computation")
296
val result = expensiveComputation.value() // "Computing..." printed here
297
298
// Memoization
299
val memoized = expensiveComputation.memoize()
300
val result1 = memoized.value() // Computes once
301
val result2 = memoized.value() // Uses cached result
302
```
303
304
### Recursive Function Utilities
305
306
Utilities for stack-safe recursive functions.
307
308
```kotlin { .api }
309
/**
310
* Create a memoized deep recursive function
311
*/
312
fun <T, R> memoizedDeepRecursiveFunction(
313
calculation: DeepRecursiveScope<T, R>.(T) -> R
314
): (T) -> R
315
```
316
317
**Usage Examples:**
318
319
```kotlin
320
// Stack-safe memoized recursive function
321
val factorialMemo = memoizedDeepRecursiveFunction<Int, Long> { n ->
322
when {
323
n <= 1 -> 1L
324
else -> n * callRecursive(n - 1)
325
}
326
}
327
328
val largeFactorial = factorialMemo(1000) // Safe for large inputs
329
330
// Tree traversal example
331
data class Tree<A>(val value: A, val children: List<Tree<A>> = emptyList())
332
333
val sumTree = memoizedDeepRecursiveFunction<Tree<Int>, Long> { tree ->
334
tree.value.toLong() + tree.children.sumOf { callRecursive(it) }
335
}
336
```
337
338
## Advanced Patterns
339
340
### Function Pipeline
341
342
Create complex data processing pipelines.
343
344
```kotlin
345
val dataProcessor = { input: String ->
346
input.trim()
347
}
348
.andThen { it.lowercase() }
349
.andThen { it.split(" ") }
350
.andThen { words -> words.filter { it.isNotEmpty() } }
351
.andThen { it.joinToString("-") }
352
353
val result = dataProcessor(" Hello World ") // "hello-world"
354
```
355
356
### Conditional Composition
357
358
Apply functions conditionally.
359
360
```kotlin
361
fun <A> ((A) -> A).applyIf(condition: Boolean): (A) -> A =
362
if (condition) this else ::identity
363
364
val processor = { text: String -> text.uppercase() }
365
.applyIf(shouldCapitalize)
366
.andThen { it.trim() }
367
.applyIf(shouldClean)
368
369
val result = processor(input)
370
```