How to: Example F# application with functional flow

Friday, May 20, 2016

In a previous post on F# data access I dropped a hint about a functional way of handling invalid arguments. The code in question looked like this.

let insertLocation city state =
    if state |> String.length >= 2 
    then DataAccess.createLocation city state
    else raise (System.ArgumentException("Not a valid State"))

If you're new to the functional paradigm you may not see any issue with this code. To explain why this isn't an ideal solution let's first look at a simpler function.

let divide x y = x / y

What is the signature of that function? At first glance it takes two numbers and returns a number. In actuality it takes two numbers and returns a number sometimes. (If y is zero an exception will be thrown)

Writing a pure function

let divide x y =
    if y = 0
    then None
    else Some(x/y)
        
divide 4 2  // Some 2
divide 4 0  // None

Here we take advantage of the Option type to remove side effects from the function. This now is a function that takes two ints and returns an int option. Because this function has no side effects it is said to be a pure function.

To do anything with the return value, a pattern match is required which explicitly handles both cases and eliminates a whole class of errors.

let two = divide 4 2
match two with
| Some i -> printfn "%i" i
| None -> printfn "cannot divide by 0"

You may see what appears to be an issue with this approach. if divide takes two int values and returns an int option, how can we chain divide calls?

let four = divide 8 2
let two = divide four 2

// The expression is expected to have type
//  int
// But has type 
//  int option

Introducing the MaybeBuilder

type MaybeBuilder() =
    member this.Return(x) = Some x
    member this.Bind(option, func) = 
        match option with
        | None -> None
        | Some a -> func a

This is an example of the class syntax in F#. In this case we declare a class MaybeBuilder and two methods.

Specifically, Bind does the work to pattern match the incoming option type and pass the result to a function that accepts the underlying, or wrapped, type.

Using the MaybeBuilder

let maybe = new MaybeBuilder()

let chainedDivisionSuccess = 
    maybe {
        let! four = divide 8 2
        let! two = divide four 2
        return two
    }
// int option = Some 2   

Let's break down what's happening here. We're creating an instance of our MaybeBuilder class and then using it to wrap the internals of the chainedDivision function.

We saw earlier that divide 8 2 returns Some 4 which is an int option but due to the magic of our MaybeBuilder and the let! syntax we are able to treat four as an int rather than an int option.

So what happens if one of the divide calls tries an invalid division?

let chainedDivisionFail = 
    maybe {
        let! undefined = divide 8 0
        let! two = divide four undefined
        return two
    }
// int option = None  

In this case the let! two = divide four undefined line isn't even evaluated. To see why let's look back at the MaybeBuilder

type MaybeBuilder() =
    member this.Return(x) = Some x
    member this.Bind(option, func) = 
        match option with
        | None -> None
        | Some a -> func a

In the Bind method we only execute func if option is Some. The let! basically preloads the first argument to Bind and allows us to interact with the value as though it were the underlying data type.

Using the MaybeBuilder in an application

Bringing back the initial bit of code in question we can see that it has similarities to the divide example in that it calls DataAccess.createLocation sometimes.

let insertLocation city state =  
    if state |> String.length >= 2 
    then DataAccess.createLocation city state
    else raise (System.ArgumentException("Not a valid State"))

So the function's signature appears to be string -> string -> int but we know that's a false signature because this function isn't pure. What we really want is for the function signature to be string -> string -> int option. I like to think of this as defensive coding within the function rather than the typical OO approach of the calling code requiring the defensive code. We're being much more honest about our function by returning the int option than int with a chance of an exception which won't actually show up in the method signature.

The simplest way to make this work is to create a helper function that returns an option.

let validateState state = 
    if state |> String.length >= 2
    then Some state
    else None

let insertLocation city state = maybe {
    let! validatedState = validateState state
    return DataAccess.createLocation city state
    }

Putting it all together

So what does the full application look like now? We've added the MaybeBuilder code and made sure that our [<EntryPoint>] function returns an int because we are now forced to handle the case where the data access logic isn't called.

//Maybe.fs
module Maybe =
    type MaybeBuilder() =
        member this.Return(x) = Some x
        member this.Bind(option, func) = 
            match option with
            | None -> None
            | Some a -> func a

    let maybe = new MaybeBuilder()

//DataAccess.fs
module DataAccess =
    open FSharp.Data

    type Create = 
        SqlCommandProvider<
            "INSERT INTO Locations(City, State)
            values(@city, @state)", 
            "name=local">

    let createLocation city state =
        use command = new Create()
        command.Execute(city, state)

//Business.fs
module Business =
    open Maybe 

    let validateState state = 
        if state |> String.length >= 2
        then Some state
        else None

    let insertLocation city state = maybe {
        let! validatedState = validateState state
        return DataAccess.createLocation city state
        }

//Program.fs
module Main =

    [<EntryPoint>]
    let main argv = 
        let result = Business.insertLocation "Knoxville" "TN"
        match result with
        | Some i -> i
        | None -> 0