An Async Task Result computation in F#

  • I think a lot of us F# programmers inevitably reach a point in our applications where we write our own result type for certain operations. Afterall, creating our own discriminated unions to represent different states is one of the most powerful features of the language.
  • It starts off with good intentions: we just want to know if a function completes, fails, or does something else. For example, I wrote my own custom result type when my functions could have three possible results: Continue to the next function, ReturnEarly in some cases, or give us a ValidationError message:
type MyResultType<'a> = 
    | Continue of 'a 
    | ReturnEarly 
    | ValidationError of string
  • The type itself isn’t very complicated, but then we often start chaining the functions that use them together. We want to be big brain functional programmers and adhere to the railroad-style of programming where we continue down the “track” of functions as long as they keep returning a good result, or we exit early when they don’t.
let stepOne () = 
    // do something here
    Continue "some string value or something"

let stepTwo strValue = 
    // do something else here
    Continue 117 

let stepThree strValue = 
    // do another thing here 
    Continue ()

But chaining these together quickly turns into some crazy spaghetti code when you have to check the case of each result:

let runAllSteps () = 
    match stepOne () with 
    | ReturnEarly -> 
        ()
    | ValidationError msg -> 
        // log here
        ()
    | Continue x -> 
        match stepTwo x with 
        | ReturnEarly -> 
            ()
        | ValidationError msg -> 
            // log here
            ()
        | Continue x -> 
            match stepThree x with 
            | ReturnEarly -> 
                ()
            | ValdiationError msg -> 
                // log here
                ()
            | Continue _ -> 
                // done
                ()

These branches get out of hand quickly, so we start writing a new module with utility functions for piping this custom result type around. It’s usually a map or a bind or an andThen function:

module MyResultType = 
    let andThen fn result = 
        match result with 
        | ReturnEarly -> 
            ReturnEarly
        | ValidationError msg -> 
            ValidationError msg
        | Continue x -> 
            fn x    

let runAllSteps () = 
    let result = 
        stepOne () 
        |> MyResultType.andThen stepTwo
        |> MyResultType.andThen stepThree

    match result with 
    | ReturnEarly -> 
        ()
    | ValidationError msg -> 
        // log here
        ()
    | Continue _ -> 
        // done 
        ()
  • That makes things a lot easier to look at! The only trouble is when you need access to the values returned by several of the functions all at once. Let’s say we want to add a new function which needs the value of the first and second functions. We’d need to tuple those values together and keep passing them through, assuming the thirdFunction was written to let us do that:

Learn how to build rock solid Shopify apps with C# and ASP.NET!

Did you enjoy this article? I wrote a premium course for C# and ASP.NET developers, and it's all about building rock-solid Shopify apps from day one.

Enter your email here and I'll send you a free sample from The Shopify Development Handbook. It'll help you get started with integrating your users' Shopify stores and charging them with the Shopify billing API.

We won't send you spam. Unsubscribe at any time.