A simple example of a composition using an F# computation expression.

In a previous article, you saw how to compose an application feature from functions:

let imp =
    Validate.reservation
    >> map (fun r ->
        SqlGateway.getReservedSeats connectionString r.Date, r)
    >> bind (fun (i, r) -> Capacity.check 10 i r)
    >> map (SqlGateway.saveReservation connectionString)

This is awkward because of the need to carry the result of Validate.reservation (r) on to Capacity.check, while also needing it for SqlGateway.getReservedSeats. In this article, you'll see a neater, alternative syntax.

Building blocks #

To recapitulate the context, you have this Result discriminated union from Scott Wlaschin's article about railway-oriented programming:

type Result<'TSuccess,'TFailure> = 
    | Success of 'TSuccess
    | Failure of 'TFailure

A type with this structure is also sometimes known as Either, because the result can be either a success or a failure.

The above example also uses bind and map functions, which are implemented this way:

// ('a -> Result<'b,'c>) -> Result<'a,'c> -> Result<'b,'c>
let bind f x = 
    match x with
    | Success s -> f s
    | Failure f -> Failure f
 
// ('a -> 'b) -> Result<'a,'c> -> Result<'b,'c>
let map f x = 
    match x with
    | Success s -> Success(f s)
    | Failure f -> Failure f

With these building blocks, it's trivial to implement a computation expression for the type:

type EitherBuilder () =
    member this.Bind(x, f) = bind f x
    member this.ReturnFrom x = x

You can add more members to EitherBuilder if you need support for more advanced syntax, but this is the minimal implementation required for the following example.

You also need a value of the EitherBuilder type:

let either = EitherBuilder ()

All this code is entirely generic, so you could put it (or a more comprehensive version) in a reusable library.

Composition using the Either computation expression #

Since this Either computation expression is for general-purpose use, you can also use it to compose the above imp function:

let imp candidate = either {
    let! r = Validate.reservation candidate
    let i = SqlGateway.getReservedSeats connectionString r.Date
    return! Capacity.check 10 i r
        |> map (SqlGateway.saveReservation connectionString) }

Notice how the r value can be used by both the SqlGateway.getReservedSeats and Capacity.check functions, because they are all within the same scope.

This example looks more like the Haskell composition in my previous article. It even looks cleaner in F#, but to be fair to Haskell, in F# you don't have to explicitly deal with IO as a monadic context, so that in itself makes things look simpler.

A variation using shadowing #

You may wonder why I chose to use return! with an expression using the map function. One reason was that I wanted to avoid having to bind a named value to the result of calling Capacity.check. Naming is always difficult, and while I find the names r and i acceptable because the scope is so small, I prefer composing functions without having to declare intermediary values.

Another reason was that I wanted to show you an example that resembles the previous Haskell example as closely as possible.

You may, however, find the following variation more readable. In order to use return instead of return!, you need to furnish a Return method on your computation builder:

type EitherBuilder () =
    member this.Bind(x, f) = bind f x
    member this.Return x = Success x

Nothing prevents you from adding both Return and ReturnFrom (as well as other) methods to the EitherBuilder class, but I'm showing you the minimal implementation required for the example to work.

Using this variation of EitherBuilder, you can alternatively write the imp function like this:

let imp candidate = either {
    let! r = Validate.reservation candidate
    let i = SqlGateway.getReservedSeats connectionString r.Date
    let! r = Capacity.check 10 i r
    return SqlGateway.saveReservation connectionString r }

In such an alternative, you can use a let!-bound value to capture the result of calling Capacity.check, but that obligates you to devise a name for the value. You'll often see names like r', but I always feel uneasy when I resort to such naming. Here, I circumvented the problem by shadowing the original r value with a new value also called r. This is possible, and even desirable, because once the reservation has been checked, you should use that new, checked value, and not the original value. That's what the original composition, above, does, so shadowing is both equivalent and appropriate.

Summary #

Computation expressions give you an opportunity to compose functions in a readable way. It's an alternative to using the underlying bind and map functions. Whether you prefer one over the other is partly subjective, but I tend to favour the underlying functions until they become inconvenient. The initial version of the imp function in this article is an example of map and bind becoming unwieldy, and where a computation expression affords a cleaner, more readable syntax.

The disadvantage of computation expressions is that you need to use return or return! to return a value. That forces you to write an extra line of code that isn't necessary with a simple bind or map.



Wish to comment?

You can add a comment to this post by sending me a pull request. Alternatively, you can discuss this post on Twitter or somewhere else with a permalink. Ping me with the link, and I may respond.

Published

Monday, 21 March 2016 08:34:00 UTC

Tags



"Our team wholeheartedly endorses Mark. His expert service provides tremendous value."
Hire me!
Published: Monday, 21 March 2016 08:34:00 UTC