F# Option types Option Module enables Railway Oriented Programming


Error handling is important but can make an elegant algorithm into a mess. Railway Oriented Programming (ROP) is used to make error handling elegant and composable.

Consider the simple function f:

let tryParse s =
  let b, v = System.Int32.TryParse s
  if b then Some v else None

let f (g : string option) : float option =
  match g with
  | None    -> None
  | Some s  ->
    match tryParse s with           // Parses string to int
    | None              -> None
    | Some v when v < 0 -> None     // Checks that int is greater than 0
    | Some v -> v |> float |> Some  // Maps int to float

The purpose of f is to parse the input string value (if there is Some) into an int. If the int is greater than 0 we cast it into a float. In all other cases we bail out with None.

Although, an extremely simple function the nested match decrease readability significantly.

ROP observes we have two kind of execution paths in our program

  1. Happy path - Will eventually compute Some value
  2. Error path - All other paths produces None

Since the error paths are more frequent they tend to take over the code. We would like that the happy path code is the most visible code path.

An equivalent function g using ROP could look like this:

let g (v : string option) : float option =
  |> Option.bind    tryParse  // Parses string to int
  |> Option.filter  ((<) 0)   // Checks that int is greater than 0
  |> Option.map     float     // Maps int to float

It looks a lot like how we tend to process lists and sequences in F#.

One can see an Option<'T> like a List<'T> that only may contain 0 or 1 element where Option.bind behaves like List.pick (conceptually Option.bind maps better to List.collect but List.pick might be easier to understand).

bind, filter and map handles the error paths and g only contain the happy path code.

All functions that directly accepts Option<_> and returns Option<_> are directly composable with |> and >>.

ROP therefore increases readability and composability.