Skip to content


Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?

Latest commit


Git stats


Failed to load latest commit information.

"Refactoring F#"

Examples from my "Refactoring F#" talk.

Code available at

Key principles of functional programming

  • Separate code from data
  • Every function has an output -- this is important for composition
  • Functions should not have hidden side-effects

Functional Design Guidelines

  • Composition everywhere
    • E.g. Convert everything to the same "level" using lifting - then composition is easier.
  • Parameterize all the things
    • E.g. remove dependencies on global code by passing in functions
  • There is no problem that cannot be solved by wrapping it in a type
  • Model the system like a compiler:
    1. Parse the (untrusted) input into a trusted internal representation (use Option.ofObj, ofNullable, etc)
    2. Process and transform the data
    3. Emit data back to the untrusted world: transform domain types back to strings and ints.

Tips for refactoring from imperative code

  • Replace throwing exceptions with returning a Result. Chain results together using "bind". See
  • Change void-returning methods to return something.
  • Use types to remove the need for validation. Obviously no nulls, but also e.g PositiveInteger
    • Pass the buck! It's the caller's responsibility to give you valid data
  • Replace inheritance with discriminated unions where appropriate.
  • Replace if/then/else tests with pattern matching -- it helps you detect edge cases
  • Use Active patterns to make conditional logic clearer

Tips for converting imperative loops

  • If you are iterating over all the elements, use fold
  • If you need to break early, use recursion, or fold with a flag.
  • Take full advantage of the built-in collection functions -- see
  • Don't forget choose and pick
  • Don't treat lists like indexed collections

General Tips

  • Using Some/None matching with options can generally be replaced with or Option.bind.
  • Use #IComparable, etc., instead of ugly type constraints
  • Booleans
    • If you see boolean flags in a data structure, chances are it's a state machine. Use a DU instead.
    • If you see a function returning a boolean, replace the result with something useful.

Code smells

  • Ignoring the output of an expression
  • Throwing exceptions rather than returning error types.
  • Returning unit rather than something useful.
  • Null checks in the core domain -- these should only be done at the boundary
  • Wildcards in pattern matching
  • Primitive obsession: Using strings and ints everywhere, rather than exploiting the type system.
  • Accessing "global" functions and state rather than having them be passed in as parameters
  • Treating lists like indexed collections
  • Treating lists as appendable collections
  • Over-reliance on "if" expressions and booleans rather than using pattern matching.
  • Over-reliance on booleans (see tip above)
  • Unwrapping and rewrapping options using "IsSome" rather than using staying in the world of options and using and Option.bind.
  • Having deeply nested lambdas - I prefer the use of private helpers


Examples from my "Refactoring F#" talk.







No releases published


No packages published