fp-best-practices
Skill5 starsChanged 30 days agoMITView on GitHub
Use when designing pure functions, functional composition, immutable data, Option/Result types, exhaustive variants, or effect boundaries. Review clarity and actual failure cases before adding functional abstractions; preserve the language and error conventions of the project.
In this repository11 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- boostSkill
- data-migration-best-practicesSkill
- ddd-best-practicesSkill
- design-patterns-best-practicesSkill
- flowSkill
- infrastructure-designSkill
- oop-best-practicesSkill
- refactoring-best-practicesSkill
- rest-api-best-practicesSkill
- tdd-best-practicesSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
39 rules. About 0 are common, 13 are distinctive, and 26 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈13
Distinctive · estimate
This author's own choices. Worth a look.
26
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 12
Use this skill for everyday functional programming decisions that shape composability, predictability, and long-term maintainability.
- Distinctive: line 14
Use it especially when the task benefits from:
- Distinctive: line 24
Write functions for the next reader, not just for the runtime.
- Distinctive: line 25
Keep side effects at the edges.
- Distinctive: line 26
Prefer small, composable functions over clever all-in-one implementations.
- Distinctive: line 27
Use types and names to reveal intent.
- Distinctive: line 45
Check types and edge cases.
- Distinctive: line 67
Use a pipeline when it clarifies data flow.
- Very specific: line 73
Use Maybe/Option when a value may legitimately be absent.
- Very specific: line 74
Use Either/Result when an operation may fail and the caller needs the error.
- Very specific: line 80
Use Object.freeze, readonly, immutable records, or persistent data structures.
- Distinctive: line 92
Prefer pure functions. Extract impure operations to the boundary.
- Very specific: line 94
Use pipe (left-to-right) or compose (right-to-left) to express data flow.
- Distinctive: line 98
Prefer immutable values; keep any necessary mutation private to its owner or explicit at an effect boundary.
- Distinctive: line 99
Prefer transformations that reveal intent.
- Distinctive: line 100
Keep functions small — one transformation, one level of abstraction.
- Distinctive: line 105
Every function can be tested with just its inputs — no setup needed.
- Very specific: line 123
Read references/typed-boundaries.md when choosing stronger types, validating external data, or weighing local mutation against a pipeline.
- Very specific: line 125
Read references/core-principles.md for the fundamental FP principles: purity, immutability, referential transparency, and composition.
- Very specific: line 126
Read references/naming-and-abstractions.md when naming or abstraction quality is the main issue.
- Very specific: line 127
Read references/function-composition.md for pipe, compose, currying, partial application, and point-free style.
- Very specific: line 128
Read references/algebraic-data-types.md for Maybe/Option, Either/Result, tagged unions, and making illegal states unrepresentable.
- Very specific: line 129
Read references/managing-side-effects.md for IO boundaries, Reader/Writer/State patterns, and effect systems.
- Very specific: line 130
Read references/higher-order-functions.md for map, filter, reduce, flatMap, transducers, and functor/monad patterns.
- Very specific: line 131
Read references/language-examples.md for an index of language-specific example files and a quick reference table.
- Very specific: line 132
Read references/javascript-examples.md for examples in JavaScript (native, Ramda, fp-ts).
- Very specific: line 133
Read references/typescript-examples.md for examples in TypeScript (fp-ts, Effect-TS, branded types).
- Very specific: line 134
Read references/python-examples.md for examples in Python (functools, itertools, returns, pattern matching 3.10+).
- Very specific: line 135
Read references/go-examples.md for examples in Go (first-class functions, generics, functional options, (value, error)).
- Very specific: line 136
Read references/rust-examples.md for examples in Rust (Iterator trait, Option/Result, enums, newtype, ?
- Very specific: line 137
Read references/haskell-examples.md for examples in Haskell (pure FP reference, typeclasses, IO monad).
- Very specific: line 138
Read references/elm-examples.md for examples in Elm (frontend FP, The Elm Architecture, decoders).
- Very specific: line 139
Read references/clojure-examples.md for examples in Clojure (threading macros, persistent data structures).
- Very specific: line 140
Read references/fsharp-examples.md for examples in F# (computation expressions, discriminated unions, pipelines).
- Very specific: line 141
Read references/elixir-examples.md for examples in Elixir (pipe operator, pattern matching, with, OTP).
- Very specific: line 145
Use oop-best-practices when the codebase mixes FP and OOP and object design is in question.
- Very specific: line 146
Use refactoring-best-practices when introducing FP into an existing imperative codebase.
- Very specific: line 147
Use design-patterns-best-practices when evaluating whether a pattern maps to a functional equivalent.
- Very specific: line 148
Use tdd-best-practices for test-driving pure functions and effect boundaries.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
FP Best Practices
Use this skill for everyday functional programming decisions that shape composability, predictability, and long-term maintainability.
Use it especially when the task benefits from:
- clearer separation between pure logic and side effects
- smaller functions composed into pipelines
- types that make illegal states unrepresentable
- data transformations over mutations
- referential transparency and easier testing
Working Style
- Write functions for the next reader, not just for the runtime.
- Keep side effects at the edges; keep the core pure.
- Prefer small, composable functions over clever all-in-one implementations.
- Use types and names to reveal intent.
- Let the data structure do the work when it can.
Review Workflow
- Identify the transformation.
- What data goes in? What comes out?
- What domain concept is this function modeling?
- Check purity.
- Does this function have observable effects (I/O, changes to caller-owned or shared state, randomness)?
- Can the side effects be pushed to the boundary?
- Check composition.
- Is this function doing one thing?
- Can it be expressed as a composition of smaller functions?
- Is it reusable in other pipelines?
- Check types and edge cases.
- Are null/undefined/error cases handled explicitly via ADTs?
- Is the return type honest about what might fail?
- Apply the lightest useful improvement.
- extract function
- introduce pipe/compose
- replace null with Maybe/Option
- replace throws with Either/Result
- push side effect to boundary
- replace loop with map/filter/reduce
Additional Review Lenses
Purity and referential transparency
- A pure function returns the same result for the same inputs, always.
- Pure functions are trivially testable, composable, and parallelizable.
- Impure operations (I/O, logging, random, time) belong at the edges of the system.
Composition over imperative sequencing
- Use a pipeline when it clarifies data flow; keep named intermediate steps or a local loop when they are easier to understand.
- Small functions combined with
pipeorcomposereveal the shape of the transformation. - Point-free style removes noise when the data flow is already clear from the pipeline.
Algebraic data types over null and exceptions
- Use
Maybe/Optionwhen a value may legitimately be absent. - Use
Either/Resultwhen an operation may fail and the caller needs the error. - Model domain variants as tagged union types (sum types) rather than optional fields.
Immutability as the default
- Preserve caller-owned data and shared state. Private accumulators can be observably pure when they do not escape.
- Use
Object.freeze,readonly, immutable records, or persistent data structures. - Mutation of caller-owned or shared state is an effect; private state that never escapes can preserve observable purity.
Higher-order functions as vocabulary
maptransforms each element of a structure without leaving the structure.filterselects elements by predicate.reduce/foldcollapses a structure into a single value.flatMap/chainsequences computations that each return a wrapped value.
Day-to-Day Rules
- Prefer pure functions. Extract impure operations to the boundary.
- Name functions by the transformation they perform, not the mechanism.
- Use
pipe(left-to-right) orcompose(right-to-left) to express data flow. - Model meaningful absence with
Maybe/Optionor the project's native equivalent; preserve established boundary contracts. - Replace
throwwithEither/Resultwhen callers need to handle failures. - Curry functions when partial application makes call sites cleaner.
- Prefer immutable values; keep any necessary mutation private to its owner or explicit at an effect boundary.
- Prefer transformations that reveal intent; a local loop is valid when a pipeline obscures the algorithm or adds material copying cost.
- Keep functions small — one transformation, one level of abstraction.
- Make state that persists between calls explicit; a closure that changes such state affects later observations.
Good Signals
- Every function can be tested with just its inputs — no setup needed.
- Pipelines read like a description of the problem.
- Illegal states cannot be constructed from the types.
- Side effects are localized to clearly named edge functions.
- Changing one transformation does not break unrelated pipelines.
Warning Signs
- Functions that mutate their inputs or external state.
nullorundefinedpassed as a meaningful sentinel value.- Exceptions used for normal control flow.
- Long functions that mix data fetching, transformation, and output.
- Mutable shared state threaded through function parameters.
forloops wheremap/filter/reducewould make intent clearer.- Deeply nested callbacks or
.then()chains that mix logic with I/O.
References
- Read
references/typed-boundaries.mdwhen choosing stronger types, validating external data, or weighing local mutation against a pipeline. - Read
references/core-principles.mdfor the fundamental FP principles: purity, immutability, referential transparency, and composition. - Read
references/naming-and-abstractions.mdwhen naming or abstraction quality is the main issue. - Read
references/function-composition.mdfor pipe, compose, currying, partial application, and point-free style. - Read
references/algebraic-data-types.mdfor Maybe/Option, Either/Result, tagged unions, and making illegal states unrepresentable. - Read
references/managing-side-effects.mdfor IO boundaries, Reader/Writer/State patterns, and effect systems. - Read
references/higher-order-functions.mdfor map, filter, reduce, flatMap, transducers, and functor/monad patterns. - Read
references/language-examples.mdfor an index of language-specific example files and a quick reference table. - Read
references/javascript-examples.mdfor examples in JavaScript (native, Ramda, fp-ts). - Read
references/typescript-examples.mdfor examples in TypeScript (fp-ts, Effect-TS, branded types). - Read
references/python-examples.mdfor examples in Python (functools, itertools, returns, pattern matching 3.10+). - Read
references/go-examples.mdfor examples in Go (first-class functions, generics, functional options,(value, error)). - Read
references/rust-examples.mdfor examples in Rust (Iterator trait, Option/Result, enums, newtype,?operator). - Read
references/haskell-examples.mdfor examples in Haskell (pure FP reference, typeclasses, IO monad). - Read
references/elm-examples.mdfor examples in Elm (frontend FP, The Elm Architecture, decoders). - Read
references/clojure-examples.mdfor examples in Clojure (threading macros, persistent data structures). - Read
references/fsharp-examples.mdfor examples in F# (computation expressions, discriminated unions, pipelines). - Read
references/elixir-examples.mdfor examples in Elixir (pipe operator, pattern matching,with, OTP).
Related Skills
- Use
oop-best-practiceswhen the codebase mixes FP and OOP and object design is in question. - Use
refactoring-best-practiceswhen introducing FP into an existing imperative codebase. - Use
design-patterns-best-practiceswhen evaluating whether a pattern maps to a functional equivalent. - Use
tdd-best-practicesfor test-driving pure functions and effect boundaries.
Source Influences
This skill is synthesized from ideas emphasized in:
- Mostly Adequate Guide to Functional Programming — Brian Lonsdorf (Professor Frisby)
- Professor Frisby's Mostly Adequate Guide to Functional Programming
- Haskell Programming from First Principles — Allen & Moronuki
- Structure and Interpretation of Computer Programs — Abelson & Sussman
- Functional Programming in Scala — Chiusano & Bjarnason
- Domain Modeling Made Functional — Scott Wlaschin
- Grokking Simplicity — Eric Normand
- fp-ts, Effect-TS, and Ramda library documentation
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

