The Functional PR Checklist
Before submitting your pull request to a functional codebase, use this checklist to ensure your code maintains the mathematical rigor, predictability, and safety of pure functional programming (FP).
Core FP Principles & Composition
- Purity: Are functions pure by default? Are side-effects completely isolated and pushed to the boundaries of the application?
- Laziness: Are computations and side-effects deferred until absolutely necessary rather than executed eagerly?
- Pipe / Flow: Is data transformed sequentially using pipe or flow instead of nested function calls or intermediate variables?
- Point-Free Style: Is point-free style applied to improve readability without crossing into cryptic or overly abstract territory?
- Stack Safety / Recursion: If using recursion for iteration, is it tail-recursive (supporting tail-call optimization) or trampolined to guarantee stack safety and prevent stack overflow exceptions?
Types & Data Structures
- Generics: Are generics used appropriately to maximize function reusability while maintaining strict type safety?
- Option / Either: Have null, undefined, nullable
values, and untyped exceptions been eliminated in favor of
Option(for missing data) andEither(for success/failure)? - Combinators: Are
map,chain(flatMap), andfold(match) used correctly to safely transform and extract data from algebraic data types? - Bifunctors / HKTs: Are we leveraging bifunctor
properties (e.g.,
mapLeft) to elegantly transform error branches without breaking the chain? - Total Functions: Have partial functions (functions
that throw, crash, or return incomplete/empty values for certain inputs,
e.g., dividing by zero or fetching from empty arrays) been replaced by
total functions that return safe monadic contexts (like
Option) or require narrowed input types? - Representing State: Are data models constructed to
make illegal states unrepresentable (e.g. using sum types / tagged
unions to represent mutually exclusive states like
Loading | Success | Failureinstead of independent boolean flags)?
Architecture & Error Handling
- Railway Oriented Programming: Does the logic
maintain a clear separation between the “happy path” and the “error
track” (e.g., using
Either/Resultto gracefully switch tracks and bypass downstream processing)? - Validation: Is input validation comprehensive? Do we accumulate validation errors (e.g., using Applicatives/Validation types) rather than failing eagerly on the first mistake?
- Explicit Errors: Are all possible errors represented in the type system rather than relying on implicit/untyped try-catch blocks?
- Monadic Flow: Are monadic contexts preserved throughout the chain without prematurely unwrapping or leaking underlying values?
- Dependency Injection: Are dependencies explicitly
passed into functions (via arguments or the
Readermonad) to ensure referential transparency and testability?
Async Operations & IO
- Task / Lazy Async: Are asynchronous workflows
modeled as lazy containers (like
Taskor suspended functions) instead of eagerly executing asynchronous tasks (such as immediate promises or futures)? - TaskEither: Are fallible asynchronous operations
wrapped securely in a combined structure like
TaskEither? - IO: Are synchronous side-effects (such as getting
the system time, accessing global environment/state, or generating
random numbers) isolated within
IOstructures?
Quality Assurance
- Testing: Are the pure functions heavily unit-tested based on input/output predictability? Are the isolated side-effects easily mocked due to proper dependency injection?