Free Monads in Web Stack — Part I

How to use Free Monads in Scala web applications

Marcin Rzeźnicki
Iterators
7 min readJan 20, 2017

--

At Iterators we’ve had many ideas on how to build a well-structured REST application and we are constantly searching for improvement and better ways to get the job done. Recently we built a system centered around free monads. We want to share this bit of knowledge in the hope that you’ll find it inspiring, or maybe show us how to do it better. While there are lots of tutorials on Free itself, we didn’t find many that explain how to use it as a kind of glue layer in your system. All in all, your comments are more than welcome.

DISCLAIMER:

This post will not teach you what free monads are. There are a lot of materials on the Internet explaining the concept, and I’ll assume that you already know what `Free` is. In case you need a refresher, read this or this.

Composition

Let’s build a Free app (neither free as in freedom, nor free as in free beer)

By app I mean full-blown akka-http, slick, cats system with tests. Obviously we’ll keep it simple, but without skipping any important details. If you want to play with the code on your own, you can get it from GitHub. Let’s say this app will be everyone’s favorite Wombat Cuddler Job App — an app for applying for a Wombat Cuddler position on Flinders Island (and no, we didn’t build an app like that. Shame).

So let’s start with a simple template, as we usually do — the entry point to our application.

… And now what? Well, our advice is this:

If you want to build your app around Free, you should start with use-cases — business logic, if you will — and turn these into ADT that you will later write interpreters for.

Forget about services, repositories, and routers. You need to build your language on which you will add all the infrastructure a modern app is going to need.
So, having said that, what’s our use case? Obviously, we need to handle job applicants who are dying to apply for this dream job. Now, we really want a systematic app organization, so let’s go with, say, com.theiterators.wombatcuddler.actions — the package where you’ll keep your functional gems. Let’s start simple and abstract at the same time: by creating Cuddlers trait where we’ll be describing actions that a (prospective) cuddler can perform. We want to register applications from wannabe cuddlers, preferably by accepting some form of request with all the needed bits, validate if this is a unique application (otherwise everyone would be flooding you with their requests), save it to some sort of storage and return, say, a public identifier known only to the one who’s just applied with which he can update his application later on, etc. Easy enough, but we really have to express this “action” by combination of primitive “instructions” (that is, case classes) representing our DSL. That is why we need to form a base language and then write a program in that language (for now, not worrying about the interpretation).
Validations are static things — they take a request and return something along the lines of Either[Error, Request], so we may want to keep them as simple methods.
What we need to abstract out are operations that we do not yet know how to execute (that will be the job of interpreter) — save request, update request, possibly remove request (if someone is insane enough to resign). Let’s see what it looks like:

Notice a little helper type Program and execute method — they will help us to write fine DSL to describe our programs. Let’s write our first simple program. In terms of our primitives, we need to validate request and call the abstract Save method:

Why Either everywhere?

We need to embrace failure. All these actions that we’ll have to implement at some point can fail (they’ll probably be using a DBIO or whatever), so it’s crucial to make it easy to signal and handle failure in abstracted logic. We, being functional programming advocates, do not really like exceptions. Besides, you can’t really use exceptions in Free because our little programs just build a structure describing a computation that’s executed somewhere else. If the program threw an exception, it’d manifest itself during interpretation, greatly surprising a programmer writing an interpreter. So your only choice is to handle failure by wrapping it in an appropriate data structure. There are other obvious advantages of this approach:

* it’s type-safe,

* it’s explicit,

* you do not need to write any error-handling code yourself (see below)

In parallel to building a business-level DSL, we’re building our neat “internal” DSL for dealing with Free as a metaphor of “program”. I hope it reads well.
We can, for instance, lift values to Free by calling pure. This way we’ll save our future interpreter a bit of work — it won’t have to validate requests (and any interpreter will benefit from that). Free.pure is very much like immediate return without executing anything, so we can call it, for instance, returns.
OK, that was easy, let’s do update. The first trouble is that our language is not sufficient to do the job — we’d like to check if email matches a PIN here (to save interpreters some work, remember) — but since these are opaque operations we have no ways to do that. We’ll need to extend our language. Also, since there are a bunch of Eithers we need to handle, we could really save ourselves some trouble and wrap our Program (which is Free, which is a monad :-)) in EitherT to handle for us failing the program if something’s gone wrong. And what does this “handling for us” mean? Whenever a Left(x) is processed, the program stops its execution and returns that to its caller.
We’re going to create a type alias over this type ProgramThatHandlesErrorsAutomatically[A] = EitherT[Program, Error, A]. While the name above surely reflects what it does, it is a bit unwieldy, so let’s call it ProgramEx instead :-).

DISCLAIMER:

To fully use the techniques presented from now on, add
addCompilerPlugin(“com.milessabin” % “si2712fix-plugin” % “1.2.0” cross CrossVersion.full)
to your build file. You could probably avoid it if you really had to, but I see no point in tormenting yourself by fighting dumb stock compiler.

And, accordingly, delete

Why do we need EitherT stuff?

EitherT is a so-called monad transformer. You can read about monad transformers in-depth here or here. We could have used Program[Either[Error, A]] all the way though. But you’d quickly discover it becomes really unwieldy since Program doesn’t know it operates on Eithers, so it would have been up to you to unwrap it from within your program flow and, based on its value, fail or proceed. EitherT, being monad transformer, not only preserves all properties of its underlying monad, but also, knowing that it can expect only Either values, can eliminate this boilerplate by making the correct decision for you. In a way, it combines ‘fail-fast’ Either behavior with Free (or any other) monad behavior. An equivalent program written without EitherT would look like a mess (I’m not even sure if it compiles):

A piece of advice stemming from what we’ve learnt so far:

Always use monad transformers with Free monads — they go well together. But do not write things like EitherT.fromEither(req.validate) in your programs — it quickly becomes line noise. Wrap transforming operations in internal DSL and you’ll achieve clean flow.

Also:

If your program is Free, then your program wrapped in EitherT is the equivalent of the program “throwing exceptions” (but in a functional way). In other words, use EitherT if you want to bail out at the first error.

Testing

At this point we’ve achieved a thing we would kill for at Iterators — our code is testable right away. That should come as no surprise if you read about Free monads — that’s one of their main selling points, after all. You do not need a database, model; heck, you do not even need a mock library. You can just write an interpreter for your program and see if you got the logic right. Like this:

So, you use a bare-bones foldMap, write a completely bogus interpreter (a mock, if you will, but without all these dark ceremonies of verify and returning and whatnot), fake everything (even types) — but in the end you can see if your logic is right. It becomes more and more important as the logic becomes more twisted that you can test it at any time with minimal effort.

That’s a very desirable property, but Free monads can offer you much more. Stay tuned for the second part of the series, where I’ll show how we built our good ol’ Service layer and how to tie it up with HTTP resource. When we finally get an end-to-end example, we’ll complicate matters even further…

Thanks for reading! If you enjoyed it, hit heart button below. Would mean a lot to me and it helps other people see the story.

Check out our website iterato.rs and subscribe for more tech goodness!

--

--

Marcin Rzeźnicki
Iterators

Using Scala to build most composable and concise business logic @ iterato.rs