FP Refine
Refactor existing imperative code toward explicit data flow, immutable state,
typed outcomes, exhaustive dispatch, and declarative domain rules. Preserve
observable behavior unless the user explicitly authorizes a behavior change.
FP is a lens, not the final judge of code quality. Apply it only when the
resulting control flow, state ownership, and domain rules become easier to
inspect than the original code.
Core workflow
- Read repository instructions, nearby code, tests, and dependency manifests.
- Identify the language and read the relevant language adapter if one exists:
- Python: references/languages/python.md
- Rust: references/languages/rust.md
- TypeScript/JavaScript: references/languages/typescript.md
- Java: references/languages/java.md
- Common Lisp: references/languages/common-lisp.md
- Identify the highest-leverage imperative pressure point:
- mutable state machine;
- long workflow with hidden data dependencies;
- string or type-test dispatch;
- scattered validation;
- exception-driven expected control flow;
- deep mutation of shared collections.
- State the intended invariant, target shape, and clean-code risks before
editing.
- Make one coherent transformation at a time.
- Run the narrowest reliable tests after each transformation.
- Run broader verification only for changed risk, missing coverage, a failure,
or an existing required gate; passing a focused test alone does not trigger it.
Clean-code vetoes
Stop, choose a smaller change, or switch to clean-code-refine when the FP
transformation would likely:
- make functions longer or raise cyclomatic/cognitive complexity;
- replace obvious code with framework, macro, type-level, or point-free
cleverness;
- fight the language's idioms or the repository's established style;
- obscure error paths, logging, tracing, or debugging;
- introduce dependency, performance, allocation, or API costs that the task did
not justify;
- split code into so many tiny functions that the domain story becomes harder
to follow.
When a broader review is requested, or when FP conflicts with small functions,
idiomatic style, testability, or simplicity, use clean-code-refine as the
orchestrator and treat this skill as one aspect pass.
Target shapes
- Model state transitions as
transition(state, event) -> Result[new_state, error]
backed by an explicit transition table or an idiomatic exhaustive match.
- Model workflows as named, typed stages composed by a small executor only when
the original workflow has real hidden dependencies.
- Replace open-ended string dispatch with enums, algebraic data types, tagged
unions, or sealed hierarchies and exhaustive handling.
- Express validation as small pure rules returning typed results, then collect
failures declaratively.
- Reserve exceptions for exceptional faults; represent expected domain outcomes
explicitly in the language's idiom.
- Return new immutable values instead of mutating shared structures in place
when that improves local reasoning.
- Represent stable business policy as validated data when that makes changes
local, reviewable, and exhaustive.
Selection rules
- Prefer the smallest transformation that removes a real hidden dependency.
- Follow existing functional libraries and house style before introducing a new
abstraction.
- Do not add a bespoke FP framework when plain functions, frozen/readonly
records, enums, tagged unions, and explicit tables are sufficient.
- Keep names explicit. Avoid point-free style, deep transformer stacks, opaque
macros, and type-level machinery that obscures the domain.
- Do not refactor working imperative code merely for aesthetic purity.
- Stop and ask before changing a public API, persistence format, or user-visible
behavior that cannot be inferred from repository context.
Detailed patterns
Read references/pattern-catalog.md only for the
specific transformation being applied. It defines the language-neutral pressure
points, target shapes, anti-patterns, and sequencing. Then read only the
language adapter that matches the target code when syntax, idiom, or verifier
choice matters.
Completion evidence
Report the imperative pressure point removed, the invariant now made explicit,
the language-specific idiom used, the clean-code vetoes checked, the focused and
broad verification results, and any intentionally deferred areas. Do not claim
improvement solely from introducing functional vocabulary; the resulting code
must be easier to inspect, test, and modify.
1---2name: fp-refine3description: Transform imperative code toward explicit data flow, immutable state, typed outcomes, exhaustive dispatch, and declarative domain rules. Use for targeted FP-style refactoring; use clean-code-refine for broad clean-code review where FP may conflict with simplicity, idiom, function size, or complexity.4---56# FP Refine78Refactor existing imperative code toward explicit data flow, immutable state,9typed outcomes, exhaustive dispatch, and declarative domain rules. Preserve10observable behavior unless the user explicitly authorizes a behavior change.1112FP is a lens, not the final judge of code quality. Apply it only when the13resulting control flow, state ownership, and domain rules become easier to14inspect than the original code.1516## Core workflow17181. Read repository instructions, nearby code, tests, and dependency manifests.192. Identify the language and read the relevant language adapter if one exists:20 - Python: [references/languages/python.md](references/languages/python.md)21 - Rust: [references/languages/rust.md](references/languages/rust.md)22 - TypeScript/JavaScript: [references/languages/typescript.md](references/languages/typescript.md)23 - Java: [references/languages/java.md](references/languages/java.md)24 - Common Lisp: [references/languages/common-lisp.md](references/languages/common-lisp.md)253. Identify the highest-leverage imperative pressure point:26 - mutable state machine;27 - long workflow with hidden data dependencies;28 - string or type-test dispatch;29 - scattered validation;30 - exception-driven expected control flow;31 - deep mutation of shared collections.324. State the intended invariant, target shape, and clean-code risks before33 editing.345. Make one coherent transformation at a time.356. Run the narrowest reliable tests after each transformation.367. Run broader verification only for changed risk, missing coverage, a failure,37 or an existing required gate; passing a focused test alone does not trigger it.3839## Clean-code vetoes4041Stop, choose a smaller change, or switch to `clean-code-refine` when the FP42transformation would likely:4344- make functions longer or raise cyclomatic/cognitive complexity;45- replace obvious code with framework, macro, type-level, or point-free46 cleverness;47- fight the language's idioms or the repository's established style;48- obscure error paths, logging, tracing, or debugging;49- introduce dependency, performance, allocation, or API costs that the task did50 not justify;51- split code into so many tiny functions that the domain story becomes harder52 to follow.5354When a broader review is requested, or when FP conflicts with small functions,55idiomatic style, testability, or simplicity, use `clean-code-refine` as the56orchestrator and treat this skill as one aspect pass.5758## Target shapes5960- Model state transitions as `transition(state, event) -> Result[new_state, error]`61 backed by an explicit transition table or an idiomatic exhaustive match.62- Model workflows as named, typed stages composed by a small executor only when63 the original workflow has real hidden dependencies.64- Replace open-ended string dispatch with enums, algebraic data types, tagged65 unions, or sealed hierarchies and exhaustive handling.66- Express validation as small pure rules returning typed results, then collect67 failures declaratively.68- Reserve exceptions for exceptional faults; represent expected domain outcomes69 explicitly in the language's idiom.70- Return new immutable values instead of mutating shared structures in place71 when that improves local reasoning.72- Represent stable business policy as validated data when that makes changes73 local, reviewable, and exhaustive.7475## Selection rules7677- Prefer the smallest transformation that removes a real hidden dependency.78- Follow existing functional libraries and house style before introducing a new79 abstraction.80- Do not add a bespoke FP framework when plain functions, frozen/readonly81 records, enums, tagged unions, and explicit tables are sufficient.82- Keep names explicit. Avoid point-free style, deep transformer stacks, opaque83 macros, and type-level machinery that obscures the domain.84- Do not refactor working imperative code merely for aesthetic purity.85- Stop and ask before changing a public API, persistence format, or user-visible86 behavior that cannot be inferred from repository context.8788## Detailed patterns8990Read [references/pattern-catalog.md](references/pattern-catalog.md) only for the91specific transformation being applied. It defines the language-neutral pressure92points, target shapes, anti-patterns, and sequencing. Then read only the93language adapter that matches the target code when syntax, idiom, or verifier94choice matters.9596## Completion evidence9798Report the imperative pressure point removed, the invariant now made explicit,99the language-specific idiom used, the clean-code vetoes checked, the focused and100broad verification results, and any intentionally deferred areas. Do not claim101improvement solely from introducing functional vocabulary; the resulting code102must be easier to inspect, test, and modify.