# Monadic Error Handling

Explaining what a monad is has become a classic exercise. One can find hundreds of articles about the topic and this is my take. It is low on theory and focuses on motivation and mechanics. I’m using OCaml for my code examples.

# The Problem: Error Handling

Exceptions are a convenient way to handle errors in programs but some critics argue that they encourage laziness and are too hard to reason about. One alternative, recently explored in the Go language, is to make functions return a result together with an error code. This forces a programmer to deal with them at every calls site. Let’s imagine such an approach in OCaml by looking at what programming with lists would look like.

Every function that can fail returns a *result* value, indicating either the result using *Ok*, or a problem using *Error*. This is simple enough for basic list functions.

The code below is presented as a literate program. Named code snippets are introduced using *<<name>>=* and referred to by *<<name>>*. This is meta notation and not part of the actual source code. I admit that this can be confusing as monadic code also uses *>>=* as an infix operator.

<<type result>>=

type 'a result

= Ok of 'a

| Error of stringlet return x = Ok x (* could have also been named "ok" *)

let error msg = Error msg

<<list functions>>=

let hd = function (* 'a list -> 'a result *)

| x::_ -> return x

| [] -> error "hd empty list"let tl = function (* 'a list -> 'a list result *)

| [] -> error "tl empty list"

| _::xs -> return xslet null = function (* 'a list -> bool result *)

| [] -> return true

| _ -> return false

Some functions, like *null*, can’t fail and we still use a *result* value for consistency, but this is a design choice. We can put these together to implement a *length* function. Before we do, here is a simple implementation that relies on exceptions:

`<<Classic>>=`

let rec length = function (* 'a list -> int *)

| [] -> 0

| x::xs -> 1 + length xs

And here is the implementation that relies on explicit error handling. For demonstration purposes we don’t use pattern matching for deconstructing a list but use *null*, *hd*, and *tl*.

`<<List>>=`

<<type result>>

<<list functions>>

let rec length xs = (* 'a list -> int result *)

match null xs with

| Ok true -> Ok 0

| Ok false ->

( match tl xs with

| Ok xs ->

( match length xs with

| Ok n -> Ok (n+1)

| Error msg -> Error msg

)

| Error msg -> Error msg

)

| Error msg -> Error msg

At every step we need to check whether an error has occurred. If so, we return it. Otherwise we take the value and carry on. What used to be simple is now dominated by so much error handling that it is difficult to make out the simple underlying algorithm.

# Error Handling Revisited

The key observation to a better solution is that we are composing functions and match the intermediate result as part of the composition. When we abstract this into an infix operator, we regain clarity. This operator is called *bind* and usually bound to *>>=*:

<<Error>>=

<<type result>>

let bind m f = (* 'a result -> ('a -> 'b result) -> 'b result *)

match m with

| Ok x -> f x

| Error msg -> Error msglet (>>=) = bind (* left associative *)

Function *bind* has type *‘a result -> (‘a -> ‘b result) -> ‘b result*:

*bind*takes as first argument an intermediate result*m*which it inspects. If it is*Ok(a)*, it calls*f*with value*a*. Otherwise it passes on the error unchanged.*bind*takes as second argument a function that takes a plain value but returns a*result*value.

The magic of *bind* is put to work when used as an infix operator that receives the intermediate result on the left and the function on the right. The *length* function becomes:

`<<Error>>=`

<<list functions>>

let rec length xs = (* 'a list -> int result *)

null xs >>= fun zero ->

if zero then

return 0

else

tl xs >>= fun xs' ->

length xs' >>= fun n ->

return (n+1)

Function *null* provides a *bool result* value that is unwrapped by *>>=* and bound to *zero* when there is no error. Otherwise (invisibly) the error is passed on. Likewise, *tl* returns the tail of list *xs* as *‘a list result*and it is bound to *xs’* such that it can be passed to *length* recursively.

When taking advantage of pattern matching, the code can be further simplified.

`<<Error>>=`

let rec length xs = (* 'a list -> int result *)

null xs >>= function

| true -> return 0

| false ->

tl xs >>= fun xs' ->

length xs' >>= fun n ->

return (n+1)

In a real program we would still rely on pattern matching rather than using *hd*, *null*, and *tl* (which I used mostly for demonstration purposes) to decompose the argument. Now *length* would become almost as simple as the classic implementation:

`<<Error>>=`

let rec length = function (* 'a list -> int result *)

| [] -> return 0

| _::xs -> length xs >>= fun n -> return (n+1)

All error handling happens invisibly inside *>>=* and the recurring theme is *left >>= fun x -> right* where the left side produces a result that is unwrapped, bound to *x*, and consumed by *right*, which in turn will produce a *result* value. The simplist way to produce such a result is by using *return*.

# The Error Monad

The type *result* introduced above is a monad and commonly known as the error monad. A monad is defined by ingredients that we already know:

`<<Monad>>=`

type 'a t

val return: 'a -> 'a t

val bind: 'a t -> ('a -> 'b t) -> 'b t

- The monad
*‘a t*(we used*‘a result*above) - Function
*return*that turns a value into a monadic value - Function
*bind*to compose computations - Additional, domain-specific functions

Repeating the definition of the error monad in the more general notation above, it becomes:

<<ErrorMonad>>=

type 'a t

= Ok of 'a

| Error of stringlet return a = Ok a

let bind m f = match m with

| Ok a -> f a

| Error msg -> Error msg

In addtion, *return* and *bind* must be well behaved in their interaction. We will not talk about what this means here. Nonetheless, I hope that the error monad demonstrates what a monad is: a clever way to compose functions. For this reason, Monads have also been described as “programmable semikolon”.

One tip: when reading monadic code that contains *return* and *>>=* it is usually best *not* to think about what these functions do in detail and to focus on the other parts precisely because *>>=* abstracts something that we should not need to know in detail.

# Extended Example

What could go wrong? Create a directory with a given set of permissions and ownership. And if it exists already, fix permissions and ownership when necessary or fail otherwise while giving a good reason.

As it turns out, this requires at least 7 system calls: *stat*, *mkdir*, *getgrnam*, *getpwnam*, *rmdir*, *chmod*, *chown* and each can go wrong. The task concisely outlined above is easily burried in a maze of if-then-else, exception handling, and error reporting.

I am discussing the problem and a monadic solution in a blog post on Medium that extends the theme that we have seen here. The heart of the solution is function *mk* that employs the error monad:

<<mkdir.mli>>=

type 'a t = Ok of 'a | Error of stringval at

: path:string (* "/some/path" *)

-> perm:int (* 0o775 *)

-> user:string (* "root" *)

-> group:string (* "wheel" *)

-> unit t

Function *Mkdir.at* creates a directory with a given set of permissions and ownership. On success it will return an *Ok ()* value and *Error msg* otherwise.

Below is the gist of the implementation that we should be able to read even without knowing all the details:

<<mkdir.ml>>=

let (>>=) = on_success

let (//=) = on_error<<error and other basic function>>

<<defintion of always, bound to //*>>

<<checks and predicates>>

<<system calls>>let mk path perm user group =

getgid group >>= fun gid ->

getuid user >>= fun uid ->

stat path >>= function

| Some st -> (* path already exists *)

is_dir st >>= fun () ->

(has_owner uid st

//= fun _ -> chown path uid gid) >>= fun () ->

(has_perm perm st

//= fun _ -> chmod path perm) >>= fun () ->

(has_group gid st

//= fun _ -> chown path uid gid)

(* improve error message, if we have an errror *)

//= fun msg -> error "fixing existing %s failed: %s" path msg

| None -> (* path does not exist *)

mkdir path perm >>= fun () ->

(chown path uid gid

//= (fun msg -> rmdir path //* fun () -> fail msg))

(* improve error message, if we have an error *)

//= fun msg -> error "creating %s failed: %s" path msglet at ~path ~perm ~user ~group =

try (* just to avoid any surprises *)

mk path perm user group

with e ->

error "error creating '%s': %s" path (Printexc.to_string e)

# Source Code

This section just pulls all the code snippets above together into one file such that it can be compiled and used interactively. The code is available from https://gist.github.com/lindig/b2a239514711667906e4052031ae20ec.

<<monads.ml>>=

module type Monad = sig

<<Monad>>

endmodule ErrorMonad : Monad = struct

<<ErrorMonad>>

endmodule List = struct

<<List>>

endmodule Error = struct

<<Error>>

endmodule Classic = struct

<<Classic>>

end