This is not a very good separation of concerns and has many other disadvantages.

Here is an example of how many programs look like:

In Haskell e.g. this would not be possible. But how should we deal with this in an impure programming language that does not enforce side effects to be made explicit, like F# e.g.?

There are a few approaches that will be presented in this post, one of which is the free monad pattern.

We will also examine a proof of concept implementation of a Tic-Tac-Toe backend following the command query responsibility segregation pattern (CQRS) together with event sourcing (ES).

See how you can implement a program in F# that is entirely pure and free from any effects and side effects!

Why should I care about purity?

A pure function is a function, which has only one effect and that is the computation of the return value.

Why do we want our code to be pure?

The reason is simple. When we have an impure expression, it can’t be substituted with its value without changing the semantics of our program.

Here is an example:

printfn "hello world" // evaluates to ()

If we substitute printfn "hello world" with () and run the code again, there won’t be any output on the console, of course.

Whereas if we substitute 1 + 2 + 3 with 6, our program will still work as expected.

1 + 2 + 3 // evaluates to 6

Additionally, we can say that the latter example is referentially transparent. This can be (a little simplistically) defined like this:

An expression e is referentially transparent if, for all programs p, all occurrences of e in p can be replaced by the result of evaluating e without affecting the meaning of p. A function f is pure if the expression f(x) is referentially transparent for all referentially transparent x.

This means that every referentially transparent function, when called many times given the same input, will always return the same value. And there won’t be any other observable side effect that would change the meaning of the program.

Such side effects could be e.g:

Printing to the console

Getting the current system time

Throwing an exception

Generating a random number

Reading from a database

I/O in general

…

Programming with referentially transparent functions has many implications. Besides the fact that pure functions can easily be memoized, they are also much easier to reason about.

There is no hidden state, neither implicit inputs nor outputs, that we have to keep in mind. Also there are no exceptions thrown and maybe caught somewhere (or maybe not) which would significantly increase the possible execution paths and therefore the complexity of our program.

For more information on purity and referential transparency refer to these resources:

Can I pretend that logging is not a side effect?

That means that the type system does not enforce purity as e.g. the Haskell type system does.

This definitely makes it easier especially for beginners to get started because the type system is not in the way and we can basically do I/O anywhere we like.

So yes, we can pretend that logging is not a side effect and just add logging statements wherever we like. One could even argue that logging does not change the semantics of our program and that therefore the above definition of referential transparency still holds.

However, letting logging aside for now, doing I/O anywhere in our code will lose all the benefits described above. It will lead to much less reasonable code that cannot be considered functional and will also break the separation of concerns principle.

Dependency injection is not sufficient

Let’s look at an example. Consider the domain of Tic-Tac-Toe that is designed with events and commands. Somewhere there might be a function like this:

Concerns are very intermingled. Almost every other line has a side effect.

There is an implicit input (let game = Repo.findBy gameId) and an implicit output (Repo.appendEvents events) as well as additional implicit outputs via exceptions.

Let’s look at some ways to improve this code.

Inversion of control

We could inject the dependencies by passing the repository functions as parameters. Following inversion of control will significantly improve the code not only because it will make it testable.

Actually for me this has always been one of the major guidelines when designing programs.

But apparently this is not good functional design. As Mark Seeman describes, this would not be possible in Haskell (which is said to be a language that to some extend enforces good functional design) without also making the handle function impure (see Functional architecture is Ports and Adapters).

Make inputs and outputs explicit

We can do better.

And it’s a simple thing though not always very easy to apply.

We don’t pass in a potentially impure function that is responsible of doing I/O. Instead we will just pass in the actual values as parameters. And instead of writing the result to a database, we will just return it.

This way we will give away the responsibility of doing the impure things completely to the caller.

Inside every function with side effects is a pure function waiting to get out.

Ideally we now have to take a look at the calling function and apply the same refactorings until we get to the top level caller.

This approach has some limitations though.

What do we do when we reach the top level? Are we done?

What if we just cannot manage to pull out any more side effects?

IO Monad: Separating the program definition from the execution

One way to deal with the impure code that is left would be to use an IO monad.

The idea is to separate the concern of defining the program from the concern of running it.

I don’t want to go into too much detail. Refer to this post for a very nice explanation with samples in Scala.

It can be encoded in F# (see this example). It is also possible to stack other effects (e.g. optional values, exceptions, async, … etc.) on top of it. But this has to be hard coded in F# because F# supports neither monad transformers nor type classes (see same example from above for a combination with optional values).

The free monad pattern

It will allow us to recursively chain operations together and pass on results to subsequent operations.

Additionally we are very flexible with what kind of effects we will use. Things like e.g. error handling, using optional values, or asynchronous operations can be baked in later. I think this is pretty amazing.

Here are two articles that go into more detail on how the free monad pattern can be implemented in F#:

TicTacToe.Interpreters.fsx

The first thing here is the definition of a few helper functions for the effect type of our Tic-Tac-Toe program.

I chose to use the AsyncResult<'T> type that is provided by the library Chessie.

All interpreters must return this type (which is is one of the downsides of the free monad pattern).

Each interpreter is defined by a function that takes a DSL and returns an Effect<'a>.

I used an alias for AsyncResult because I thought it would have some benefits and make it easier to change the result type later if necessary, but I’m not sure about that any more at this moment.

However, the interpreter matches on the input parameter to extract the type and the input arguments as well as the continuation. Then the continuation will be called on the result of whatever is computed.

Here is an example of a pure interpreter that interprets the core domain DSL. It just calls the implementation of the core domain:

Or a docker container with a shared directory can be started e.g. like this:

docker run -it -p 8080:8080 -v $(pwd):/tmp/ttt fsharp/fsharp bin/bash

Some remarks about the implementation

Subscribing to events

I had some difficulties along the way while implementing the subscription to the event bus.

At first the event bus DSL had a definition for subscribing to it. This seems the right approach because that’s what you do with an event bus if you want to consume events.

The result of calling the subscribe function can be unit. But what should it take as an input parameter? Maybe Event -> unit seems a good fit.

When implementing the interpreters I found out that this just doesn’t work. There is no way I can get a value of type Event -> unit from a subscriber such as the read model because that would make the program potentially impure.

Maybe there are other and better solutions, but at the end I defined a union case for the read model that describes the subscription to the event bus and has the type unit -> unit.

Therefore the subscription is entirely taken care of within the interpreter. Only the subscription has to be interpreted before executing the other programs. In this case right before starting the web server:

interpret (ReadModel.subscribe())
startWebServer config app

Publishing events

The publish function actually takes a list of events to publish. Publishing events individually can’t be done right now.

For that reason and in general, mapping a function of type 'a -> Free<'b> over a list, an option or a result would need some additional helper functions.

Composition

Due to missing type classes it is not possible to compose existing programs together without any hard dependencies.

If we wanted to make the application layer of the Tic-Tac-Toe backend more pure by implementing the free monad pattern there as well, the only way I see right now is to model the Tic-Tac-Toe domain as an effect and interpret it within the interpreter of the application layer.

Getting the types right

Also getting the types right while defining the combined interpreter was a bit tricky, but worked out well at the end.

Error handling

Error handling can be improved a lot. The error type should not be string. And there is no mechanism for notifying users of domain errors.

Conclusion

The free monad pattern is a very powerful concept because it allows to write pure programs and follows a very strict separation of description, interpretation and execution of a program.

If e.g. interpreters have to be changed, the program does not have to be touched at all.

It has some disadvantages as it needs some boiler-plate code e.g. and it doesn’t compose well with other programs due to missing type classes in F#.

The implementation presented here is just an experiment and represents work in progress. It is my first shot at it and there is probably a lot of room for improvement. If you have any suggestions of how to make the implementation better and cleaner, please let me know, comment here or feel free to send a pull request.

Also if you have any questions, please post a comment. I’d be happy to try to answer them.