The Functional PR Checklist
Invariants required before merging code into a purely functional codebase.
Core FP Principles & Composition
- Purity by Default: Functions are pure. Side-effects are pushed to the absolute edge of the system (IO layer).
- Deferred Evaluation: Computations and effects are deferred (lazy) until execution is mandatory.
- Compositional Flow: Data transforms sequentially
via function composition (
pipe/flow), not through re-assigned intermediate variables. - Readable Point-Free: Point-free style is applied only where it clarifies data flow; avoid esoteric combinator puzzles.
- Stack Safety: Iteration via recursion is strictly tail-recursive (TCO) or trampolined to eliminate stack overflow failure modes.
Types & Data Structures
- No Nulls, Undefined, or Raw Exceptions: Missing
values use
Option/Maybe; fallible operations returnEither/Result. - Illegal States Unrepresentable: Mutually exclusive
states are modeled as tagged sum types
(e.g.
Loading | Success | Failure), never independent boolean flags. - Total Functions Only: No unhandled partial functions (e.g., division by zero, unguarded head/index lookups). Return safe monadic contexts or require refined input types.
- Safe Combinators: Algebraic data types are unpacked
via
map,chain(flatMap), andfold(match), never through manual structural unwrapping.
Architecture & Error Handling
- Railway-Oriented Flow: Logic maintains clear
segregation between the happy path and the error track via
Either/Result. - Accumulated Validation: Input validators accumulate errors across all invalid fields (Applicative validation) rather than aborting eagerly on the first failure.
- Explicit Domain Errors: All possible failure paths are declared within the type system, not hidden in untyped try/catch handlers.
- Referential Transparency: External dependencies
(time, entropy, databases) are passed explicitly into functions or
injected via the
Readermonad.
Async Operations & IO
- Lazy Async Containers: Asynchronous operations are
wrapped in lazy tasks (e.g.,
Task, suspended thunks), never immediate eager promises. - Isolated IO: Synchronous side-effects (reading
clocks, generating UUIDs, reading environment variables) are
encapsulated within
IOboundaries. - Deterministic Testing: Pure units are verified through input/output assertions with zero mock frameworks. Side-effects are tested through explicit dependency parameters.