First foray into Computed Expressions

The last day has involved a lot of learning, bringing together concepts which had previously been ‘academic’ with some experience in what I now understand to be specific instances of computed expressions/monads, such as binding and promises in JavaScript and async from C#.

Please note this is based on my best understanding so far, but given my limited experience in the subject it may be somewhere between misleading and outright wrong in places. Nevertheless, I hope the way I arrived at the monad destination might help others.

I started this quest trying to solve an indentation problem:

match IsValid template with
| (false, reason) -> 
    Logger.warn reason
    raise (InvalidTemplate reason)
| _ ->
    match repo.FindById template.Id with
    | Some _ -> 
        Logger.warn (sprintf "Duplicate id %O" template.Id)
        raise (DuplicateId)
    | None ->
        repo.Save template               

There are multiple problems with this snippet:
1. This is horribly unreadable, and the more clauses that might return errors there are the worse it is going to get.
2. To provide early return I’ve used exceptions, which are really just glorified gotos and are not ‘with the grain’ of F#

Searching for solutions to this problem led me to computation expressions – a concept I had not yet reached in Expert F# 4.0.

Computation expressions are syntactic sugar, i.e. an abstraction, for handling continuation functions. A continuation function is something that is called to continue executing something (an algorithm say). It is familiar to me from JavaScript, where we provide continuations as callbacks for async activities like Ajax requests or (in Node.js) IO. When I first worked with Node.js I recall (naively) having the same problem – the callback hell where multiple layers of IO calls descended into a nasty pattern of indentation:

fs.readFile(file1, 'utf8', function (err, data) {  
    ...
    fs.writeFile(file2, data, function (err) {
        ...
    });
});

Since then I’ve discovered promises which turn this into a pattern without indentation, but that still involves a lot of functions:

// psuedocode only
asyncReadFile()
.then(function(err, data){
    return asyncWriteFile(...);
})
.then(function(){...})
.then(function(){...})

C# and TypeScript go a step beyond this by introducing keywords, async and await, that hide the wrapping that turns functions into Tasks/Promises. For instance, in the following C#, the GetIntAsync function signature indicates it returns a task, but the body is written to return an int (return 1) and the caller can directly use the result of the function as shown in the function Intermediate(): return await GetIntAsync(). The Main function shows what the await syntax is hiding.

static async Task<int> GetIntAsync() 
{
    await Task.Delay(5000); //pretend we're waiting for something external
    return 1;
}

static async Task<int> Intermediate()
{
    return await GetIntAsync();
}

static void Main(string[] args)
{
    // Equivalent of await, but we can only do it in async methods so it is written out fully here
    Task<int> task = Intermediate();
    task.Wait();
    int result = task.Result;
}

Computation expressions provide a general way to alter the flow of a program between expressions (statements). To do this a generic wrapper class is required, and an instance of such a class is called a builder. In the above C# example, the generic Task is like a computation expression wrapper, async/await are the syntactic sugar, and the builder is built into the compiler.

The detail of computation expressions is an extensive subject that I’m not going to go further into here. For that I highly recommend the F# for fun and profit series to give an in-depth understanding. Instead I will highlight how I’ve applied one. The computational expression I implemented to replace the earlier code was based on the Railway Oriented Programming concept and can be viewed in full here. The following are the relevant snippets from that code.

Implementing a Railway Oriented Programming computation expression

The Railway module contains the computation expression implementation. Each block of a railway is expected to run a function that returns a Result. If the result is a failure then the Bind method, which is called by let! and do!, will return failure and no continuation function (the argument f) will be run. If the result is a success, then f will be run, and given the value returned from the previous block. The end result is a computation expression that will process continuation functions until it receives a failure from one of them.

module Railway

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

type RailwayBuilder () =
    member this.Bind(x, f) = 
        match x with
        | Success s -> f s
        | Failure f -> Failure f

    member this.Return x = Success x
    member this.ReturnFrom x = x

let railway = new RailwayBuilder()

The next code snippet shows the railway computation expression in use. It is the block from line 30 to 41 preceded by the value railway which was defined in the last line of the Railway module.
Line 31 evaluates the expression IsValid template which returns a Result. Because of the do! syntax, this will then call RailwayBuilder.Bind with x set to the result of IsValid template. If x is Failure then bind returns Failure and no further expressions from the railway block are called. If ‘x’ is Success then bind calls the next expression in the block (i.e. line 33).

module Commands.Template

open Railway
open DomainInterfaces

type TemplateCommand =
    | Create of DomainTypes.Template
    | Update of DomainTypes.Template

type TemplateCommandError =
    | InvalidTemplate of string
    | DuplicateId

type ITemplateCommandHandler =
    abstract Execute: TemplateCommand -> ITemplateWriteRepository -> Result<unit,TemplateCommandError>

let IsValid (template: DomainTypes.Template) =
    match template with
    | { Fields = f } when (Util.isNull f) -> Failure (InvalidTemplate "Template must include a list")
    | { Fields = [] } -> Failure (InvalidTemplate "Template may not have an empty list")
    | { Id = id } when id = System.Guid.Empty -> Failure (InvalidTemplate "Template must have an Id")
    | _ -> Success ()

let TemplateCommandHandler = {
    new ITemplateCommandHandler with
        member this.Execute (cmd: TemplateCommand) (repo: ITemplateWriteRepository) =
            match cmd with

            | Create(template) ->
                railway {
                    do! IsValid template

                    let foundTemplate = repo.FindById template.Id
                    let! isDuplicate =
                        match foundTemplate with 
                        | Some _ -> Failure DuplicateId
                        | None -> Success ()

                    repo.Save template
                    return! Success ()
                }

            | Update(template) -> Success () // Not implemented
}

At the API level, our controller is now free of exceptions and uses F# much more natively.

let result = TemplateCommandHandler.Execute cmd (new TemplateWriteRepository()) 
match result with
| Failure (err: TemplateCommandError) ->
    Logger.warn (sprintf "TemplateController.Create bad request: %O" err)
    this.BadRequest(err) :> IActionResult 
| Success _ ->
    let url = new UrlActionContext (Controller = "Template", Action = "Get", Values = new RouteValueDictionary(dict [("id", box template.Id)]))
    this.Created((this.Url.Action url), "") :> IActionResult