From time to time, with strange turbulence in space-time dimensions, it happens that I need to work with the Play framework. This of course is nothing bad and I got used to it, I know its quirks and I can quickly build a microservice following the Play’s convention over configuration principles. Although Play Framework is based on Akka Streams and working with Akka is a breeze, I miss some of the common Functional Programming patterns when encountering a typical Play-based project.

In this post I will try to show you how to make your Play application to be more FP, how this can help you reason about your code, to get rid of Future referential transparency problems and more.

I thought that the best way to show how we can write a Play application differently is to take the existing Play starter application with Slick and step-by-step convert it to a more functional creature.

The Old — Good, Bad and Ugly

You can find a lot of examples on how to use Play framework in the Play Samples repository. We will focus mainly on play-scala-slick-example project which can be found here.

The bad thing about the play-scala-slick-example project is that it has no service layer which normally exists in a microservice Play-based project doing the business logic, but it’s a minor one. I guess the authors of the example projects didn’t care about that much, as it’s not needed to show the core of the functionality they try to present. Anyway, we will try to fix that with some examples later, as it will help us to show the advantages of more functional solution.

The way things work in our original example is that we have basically a model , repository and controller and you are able to modify the state using the view . My favourite thing about Play is that it has a common JSON handling mechanism, therefore if you choose Play to be your solution to write multiple microservices talking to each other, you will very rarely encounter JSON serialization and deserialization problems (which, by the way, takes usually 1/3rd of technical discussions in every other project ;) ). There are many other nice things about Play but let’s not focus on that now and move to more serious matter.

The fact that Play is using Akka (which I like) causes that we end up with Future wrapped values everywhere (which I don’t like). This has a number of serious consequences:

Futures are not referentially transparent and they memoize their values (this generates a bunch of other serious consequences)

Because they memoize their value, calling the same future reference multiple times changes nothing (but causes confusion in the code)

map , flatMap etc. on Future requires ExecutionContext to be in scope

, etc. on requires to be in scope you cannot cancel a Future once it is started

once it is started when using Future you don’t know if you are dealing with side effects or not

you don’t know if you are dealing with side effects or not they are difficult to test

they are difficult to refactor

they are difficult to reason about

And there are more. You can read about it in the excellent blog post ‘Are Scala Futures the past?’ here.

I assume the reader has an existing application, or maybe a number of microservices written in Play framework and would like to step by step move towards more functional approach, without doing a huge revolution but with small evolutions applied one by one, where all the people in the team can follow and learn. We start with very simple but powerful change.

Stage 1

The simplest thing you can do to make your Play application a bit better is to use some data structure which performs effects on evaluation, for example Cats IO.

With IO the values are pure and immutable and because of that, they preserve referential transparency. IO represents just a description of a side effectful computation. It’s much easier to test and to reason about, you just use it as any other value.

In our example project we have two methods in the repository returning Future

We can easily convert them to return IO instead and deal with IO wrapped values elsewhere in the project. This gives us one major advantage — referential transparency. Once you have your side effects wrapped in the IO monad you can forget about famous problems often encountered with Futures :

The code above will print "hey" value once, whereas with IO we can do the same and have better understanding of what’s happening:

Of course, when wrapping the Future you still need to remember about its memoization feature:

will print "hey fut!" only once! When wrapping the Future remember to wrap the block of code which creates the reference and not the reference after creation:

Example for our use case in play-scala-slick-example project would be:

This above code could work but we would need to wrap all our db calls into double wrapped IO setup. We can do better of course. Executing our slick db.run(..) methods can be wrapped with specially created util method:

The code above will solve your problem with repeating IO wraps and will convert our Future to IO using IO.async for best slick integration.

For all this to work you would need to add implicit ContextShift for our IO operations, you can either add an implicit parameter to the method or just add it in the class scope initialized from implicit ExecutionContext available in Play Framework.

That is a pretty easy change giving us a lot of benefits. Now, when we have our side effectfull operations wrapped in IO , we need to use it somehow.

The simplest approach would be to execute unsafeToFuture() in all the places which we consider our ‘end of the world’ — mostly Controller methods.

This solves our problem but it’s cluttering our controllers with many unsafeToFuture() calls. We can do a bit better than that. With a simple type class mechanism we can extend our existing Play’s ActionBuilder and use IO directly in our controllers:

The ActionBuilder example was actually borrowed from the YouTube presentation “Alexandre Delegue — Refactorer une application play en utilisant cats et cats IO!” — too bad it’s in French ;)

Having the above imported, we can simply do:

Wherever you are returning Future.successfull(..) calls, you can safely get rid of the Future itself and use IO.apply or IO.pure to do the same, for example in our controller addPerson method we can do (when encountering errors in the form submission):

For the differences between IO(..) , IO.pure(..) and others, please see the excellent Cats documentation here.

The simple change presented in the first part of this series, gives us the ability to use our IO everywhere in the Play-based project. This greatly simplifies reasoning about the code, refactoring and in the long run testing. To modify your existing project successfully to use IO in the way presented you would need to add the cats-effect dependency of course and enable the higherKinds compiler option in your build.sbt . You can find the source code for this example in the GitHub repository.

Watch this space for more tips and tricks on how to make your existing or new Play application in a more functional manner.