val createReservation : reservation:'a -> 'b

Full name: index.createReservation
val reservation : 'a
val createReservation : (obj -> obj)

Full name: index.createReservation
val validate : ('a -> 'b)

Full name: index.validate
val f : ('a -> 'b)

Full name: index.f
val g : ('b -> 'c)

Full name: index.g
val h : (obj -> obj)

Full name: index.h
type 'T option = Option<'T>

Full name: Microsoft.FSharp.Core.option<_>
val persist : ('a -> 'b)

Full name: index.persist
val persist' : ('a -> 'b)

Full name: index.persist'
val persist' : (obj option -> obj option)

Full name: index.persist'
module Option

from Microsoft.FSharp.Core
val bind : binder:('T -> 'U option) -> option:'T option -> 'U option

Full name: Microsoft.FSharp.Core.Option.bind
val bind : binder:('a -> 'b option) -> option:'a option -> 'b option

Full name: index.bind
val binder : ('a -> 'b option)
Multiple items
val option : 'a option

--------------------
type 'T option = Option<'T>

Full name: Microsoft.FSharp.Core.option<_>
union case Option.Some: Value: 'T -> Option<'T>
val value : 'a
union case Option.None: Option<'T>
val sendNotification' : (obj option -> obj option)

Full name: index.sendNotification'
val map : mapping:('T -> 'U) -> option:'T option -> 'U option

Full name: Microsoft.FSharp.Core.Option.map
val map : mapping:('a -> 'b) -> option:'a option -> 'b option

Full name: index.map
val mapping : ('a -> 'b)

Railway Oriented Programming

Functional approach to error handling

Happy path

Surely this is all that can happen...

Imperative example

1: 
2: 
3: 
4: 
5: 
6: 
public IHttpActionResult CreateReservation(ReservationDTO reservation) {
    Validate(reservation);
    PersistAndUpdate(reservation);
    SendNotification(reservation);
    return Json(reservation);
}

Functional example

1: 
2: 
3: 
4: 
5: 
let createReservation reservation =
    validate reservation
    |> persistAndUpdate
    |> sendNotification
    |> Json

Let's aim for point-free

1: 
2: 
3: 
4: 
5: 
let createReservation =
    validate
    >> persistAndUpdate
    >> sendNotification
    >> Json

But we can't have nice things

  • Validations fail
  • DB connections drop
  • SMTP servers get overloaded

Imperative

1: 
2: 
3: 
4: 
5: 
6: 
public IHttpActionResult CreateReservation(ReservationDTO reservation) {
    Validate(reservation);
    PersistAndUpdate(reservation);
    SendNotification(reservation);
    return Json(reservation);
}

Imperative

1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
9: 
public IHttpActionResult CreateReservation(ReservationDTO reservation) {
    var validated = Validate(reservation);
    if (!validated) {
        return BadRequest("Reservation invalid!");
    }
    PersistAndUpdate(reservation);
    SendNotification(reservation);
    return Json(reservation);
}

Imperative

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
public IHttpActionResult CreateReservation(ReservationDTO reservation) {
    var validated = Validate(reservation);
    if (!validated) {
        return BadRequest("Reservation invalid!");
    }
    var updatedReservation = PersistAndUpdate(reservation);
    if (updatedReservation == null) {
        return BadRequest("Unable to persist reservation!");
    }
    SendNotification(updatedReservation);
    return Json(reservation);
}

Imperative

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
public IHttpActionResult CreateReservation(ReservationDTO reservation) {
    var validated = Validate(reservation);
    if (!validated) {
        return BadRequest("Reservation invalid!");
    }
    try {
        var updatedReservation = PersistAndUpdate(reservation);
        if (updatedReservation == null) {
            return BadRequest("Unable to update reservation!");
        }
    } catch {
        return InternalServerError("DB error: unable to persist reservation!");
    }
    SendNotification(updatedReservation);
    return Json(updatedReservation);
}

Imperative

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
public IHttpActionResult CreateReservation(ReservationDTO reservation) {
    var validated = Validate(reservation);
    if (!validated) {
        return BadRequest("Reservation invalid!");
    }
    try {
        var updatedReservation = PersistAndUpdate(reservation);
        if (updatedReservation == null) {
            return BadRequest("Unable to update reservation!");
        }
    } catch {
        return InternalServerError("DB error: unable to persist reservation!");
    }
    try {
        SendNotification(updatedReservation);
    } catch {
        log.Error("Confirmation not sent!");
    }
    return Json(updatedReservation);
}

Functional?

1: 
2: 
3: 
4: 
5: 
let createReservation =
    validate
    >> persistAndUpdate
    >> sendNotification
    >> respond

What does any of this have to do with railways?

1: 
let f: 'a -> 'b = ...

Single-track function

1: 
2: 
let f: 'a -> 'b = ...
let g: 'b -> 'c = ...

Two single-track functions

1: 
2: 
3: 
let f: 'a -> 'b = ...
let g: 'b -> 'c = ...
let h = f >> g

Single-track function composition

1: 
let h: 'a -> 'c = f >> g

Single-track function composition done

Switches

Switch

1: 
2: 
let validate: Reservation -> Reservation option = ...
let persist:  Reservation -> Reservation option = ...

Switch composition

1: 
2: 
let validate: Reservation        -> Reservation option = ...
let persist': Reservation option -> Reservation option = ...

Switches composed

1: 
2: 
let persist': Reservation option -> Reservation option = 
    Option.bind persist

Bind adapter

1: 
2: 
3: 
4: 
let bind binder option =
    match option with
    | Some value -> binder value
    | None -> None
1: 
2: 
let sendNotification': Reservation option -> Reservation option = 
    Option.map sendNotification

Map adapter

1: 
2: 
3: 
4: 
let map mapping option =
    match option with
    | Some value -> Some (mapping value)
    | None -> None

And many more...

  • unit-returning functions
  • exception-throwing functions
  • inspections
  • ...

Functional "real path"

1: 
2: 
3: 
4: 
5: 
let createReservation =
    validate
    >> persistAndUpdate
    >> sendNotification
    >> respond

Result<'TSuccess, 'TError> in FSharp.Core 4.1 (coming soon)

Thank you!

Sources