Kotlin Functional Programming
Workflow
- Read the relevant reference file(s) from
references/ before advising (see References section below for which file covers which topics).
- Identify which principles apply to the user's code or question (see Summary of Principles in the reference).
- Apply the relevant best practices; cite section numbers when explaining trade-offs.
- Prefer Kotlin built-in features over third-party libraries unless the user explicitly asks otherwise.
- Validate suggestions against the Kotlin 2.0–2.2+ feature set described in the reference.
- When refactoring existing code, make the smallest change that moves toward the functional core, imperative shell architecture.
Key Principles (quick reference)
- Default to
val; treat var as a code smell requiring justification.
- Write pure functions for all domain logic; push I/O and mutation to the edges.
- Use
if, when, try as expressions that return values assigned to val.
- Model variants with
sealed interface; never add else to an exhaustive when.
- Wrap primitives in
@JvmInline value class to prevent argument-order bugs at zero runtime cost.
- Handle expected failures with
T?, Result<T>, or sealed error hierarchies — not exceptions.
- Compose behavior with higher-order functions; use
inline on lambda-taking utilities to eliminate allocation overhead.
- Build data pipelines with collection operators (
map, filter, fold, flatMap, …).
- Default to eager collections; switch to
Sequence only for large datasets with multiple chained operations.
- Use
tailrec for linear recursion, DeepRecursiveFunction for tree/graph recursion.
- Model async work with
suspend functions and Flow pipelines; prefer coroutineScope over GlobalScope.
- Use context parameters (Kotlin 2.2+) sparingly for cross-cutting concerns (
Raise, logging, transaction scope).
Common Tasks
Writing pure domain logic
Follow §2 (Pure Functions), §3 (Immutability), §17 (Functional Core, Imperative Shell).
Pass all dependencies as parameters; return new values instead of mutating inputs.
Modeling a domain with types
Follow §5 (Sealed Types), §6 (Value Classes).
Prefer sealed interface over sealed class unless subtypes share state.
Add validation in value class init blocks ("parse, don't validate").
Error handling
Follow §7 (Functional Error Handling).
- Absence/not-found → nullable
T?
- Wrapping throwing APIs →
Result<T> + runCatching
- Typed domain errors → custom sealed hierarchy + minimal
Either<L, R>
Collection pipelines
Follow §12 (Collections) and §13 (Sequences).
Prefer named operations over manual loops.
Use fold over reduce when an initial value is needed or the result type differs.
Testing
Follow §18 (Testing).
- Test pure core with direct assertions — no mocks needed.
- Test sealed branches exhaustively.
- Use Kotest
checkAll with Arb for property-based testing of validators and transformations; prefer checkAll over forAll for matcher-based assertions and clearer failures.
- Reserve integration tests (MockK, Testcontainers) for the imperative shell.
References
- references/01-foundations.md — §1–4: Why FP, Pure Functions, Immutability, Expressions
- references/02-type-system.md — §5–7: Sealed Types, Value Classes, Error Handling
- references/03-functions-and-composition.md — §8–11: HOFs, Inline, Extensions, Scope Functions
- references/04-collections-and-sequences.md — §12–13: Collection Pipelines, Sequences
- references/05-advanced-patterns.md — §14–16: DSLs, Recursion, Coroutines
- references/06-architecture-testing-context.md — §17–19: Architecture, Testing, Context Parameters
- references/07-summary.md — §20: Summary of all 17 principles
1---2name: kotlin-functional-programming3description: Guides writing idiomatic, functional-style Kotlin code using built-in language features. Use when asked to write, review, or refactor Kotlin code for immutability, pure functions, sealed types, error handling, collections, coroutines, or functional architecture patterns.4---56# Kotlin Functional Programming78## Workflow91. Read the relevant reference file(s) from `references/` before advising (see References section below for which file covers which topics).102. Identify which principles apply to the user's code or question (see Summary of Principles in the reference).113. Apply the relevant best practices; cite section numbers when explaining trade-offs.124. Prefer Kotlin built-in features over third-party libraries unless the user explicitly asks otherwise.135. Validate suggestions against the Kotlin 2.0–2.2+ feature set described in the reference.146. When refactoring existing code, make the smallest change that moves toward the functional core, imperative shell architecture.1516## Key Principles (quick reference)17- Default to `val`; treat `var` as a code smell requiring justification.18- Write pure functions for all domain logic; push I/O and mutation to the edges.19- Use `if`, `when`, `try` as expressions that return values assigned to `val`.20- Model variants with `sealed interface`; never add `else` to an exhaustive `when`.21- Wrap primitives in `@JvmInline value class` to prevent argument-order bugs at zero runtime cost.22- Handle expected failures with `T?`, `Result<T>`, or sealed error hierarchies — not exceptions.23- Compose behavior with higher-order functions; use `inline` on lambda-taking utilities to eliminate allocation overhead.24- Build data pipelines with collection operators (`map`, `filter`, `fold`, `flatMap`, …).25- Default to eager collections; switch to `Sequence` only for large datasets with multiple chained operations.26- Use `tailrec` for linear recursion, `DeepRecursiveFunction` for tree/graph recursion.27- Model async work with `suspend` functions and `Flow` pipelines; prefer `coroutineScope` over `GlobalScope`.28- Use context parameters (Kotlin 2.2+) sparingly for cross-cutting concerns (`Raise`, logging, transaction scope).2930## Common Tasks3132### Writing pure domain logic33Follow §2 (Pure Functions), §3 (Immutability), §17 (Functional Core, Imperative Shell). 34Pass all dependencies as parameters; return new values instead of mutating inputs.3536### Modeling a domain with types37Follow §5 (Sealed Types), §6 (Value Classes). 38Prefer `sealed interface` over `sealed class` unless subtypes share state. 39Add validation in value class `init` blocks ("parse, don't validate").4041### Error handling42Follow §7 (Functional Error Handling). 43- Absence/not-found → nullable `T?`44- Wrapping throwing APIs → `Result<T>` + `runCatching`45- Typed domain errors → custom sealed hierarchy + minimal `Either<L, R>`4647### Collection pipelines48Follow §12 (Collections) and §13 (Sequences). 49Prefer named operations over manual loops. 50Use `fold` over `reduce` when an initial value is needed or the result type differs.5152### Testing53Follow §18 (Testing). 54- Test pure core with direct assertions — no mocks needed.55- Test sealed branches exhaustively.56- Use Kotest `checkAll` with `Arb` for property-based testing of validators and transformations; prefer `checkAll` over `forAll` for matcher-based assertions and clearer failures.57- Reserve integration tests (MockK, Testcontainers) for the imperative shell.5859## References60- [references/01-foundations.md](references/01-foundations.md) — §1–4: Why FP, Pure Functions, Immutability, Expressions61- [references/02-type-system.md](references/02-type-system.md) — §5–7: Sealed Types, Value Classes, Error Handling62- [references/03-functions-and-composition.md](references/03-functions-and-composition.md) — §8–11: HOFs, Inline, Extensions, Scope Functions63- [references/04-collections-and-sequences.md](references/04-collections-and-sequences.md) — §12–13: Collection Pipelines, Sequences64- [references/05-advanced-patterns.md](references/05-advanced-patterns.md) — §14–16: DSLs, Recursion, Coroutines65- [references/06-architecture-testing-context.md](references/06-architecture-testing-context.md) — §17–19: Architecture, Testing, Context Parameters66- [references/07-summary.md](references/07-summary.md) — §20: Summary of all 17 principles