or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

collections.mderror-handling.mdfunctional-utilities.mdinclusive-or.mdindex.mdoptional-values.mdraise-dsl.md

functional-utilities.mddocs/

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

```