Kotlin Functional Programming II: Monad Transformers
Impressive huh? That’s exactly what you get when you collapse your Monad Stack into a single type. So I mean, why wouldn’t you do it?
If you haven’t read the first part on this series, please do it before reading this. It will help you to understand. Also, this post is a further iteration on that approach.
Something very well said on the FP world is that Monads do not compose. At least not gracefully. They just compose when you return the same monad on all the functions on a call chain, so you can use
flatten to collapse the stack. But that’s a very unlikely situation.
In a standard scenario, your functions would probably return different monads at different levels, and your stack could end up being deeply nested.
When you get many nested levels on the stack, it starts looking like a pyramid. Check out this snippet where you have 3 levels of nested monads. Here we just want to get access to the innermost level:
This approach can exponentially lead to scary situations.
On the previous snippet we were playing with
Either at all levels. It’s a single type, so we can apply something cool called Monad Comprehensions for the sugar!
If you know a bit about Haskell you have probably heard about something called “do notation”. It’s a way to write sequential code that could be “flatMapped” with a well aligned sequence of operations declared one after another. You can also “yield” a result in the end. This is just for syntax.
KΛTEGORY provides that under the monad context using something called bindings. They enable monad comprehensions for all datatypes for which a monad instance is available.
So using bindings the previous snippet could also be written like:
Bindings are run over a monad context, for example a
MonadError instance. Binding blocks can contain any number of sequential “flatMappeable” operations, declared one after another. Each operation result is automatically lifted to the monad context and “flatmapped” to the next line thanks to the
In the end you can
yield a result which will also automatically be lifted to the context of the monad being used.
This syntactic sugar feature can be found on many languages. Every time you have a bunch of sequential operations that return the same monad type and provide a final result lifted to the same context, that’s prone to use a binding.
But remember: we can do this here mainly because we are just playing with a single type:
Either. What happens when we have different nested types?
This example is part of one of the kategory.io documented datatypes: EitherT. KΛTEGORY docs are very didactic. They provide detailed samples with code snippets and further explanations.
Real world problem
You also have a deep example of this situation on the previous post, where we built our monad stack step by step to solve the main concerns for any Android app. Then we ended up having the following stack:
Reader<GetHeroesContext, IO<Either<CharacterError, List<SuperHero>>>
So there we had our composed
Reader computation expecting a context providing the required dependencies to run. When dependencies are provided, unsafe effects wrapped on
IO are performed, which return
Either an error or a valid list of heroes.
The stack was starting to get a bit complex, therefore readability decreased.
But the monad stack approach is actually incomplete. Functional developers wouldn’t stay there but iterate further, and use Monad Transformers to solve the issue.
So what’s a Monad Transformer?
It’s similar to a regular monad, but it’s not a standalone entity: instead, it modifies the behaviour of an underlying monad.
Most of the monads provided by KΛTEGORY have transformer equivalents.
By convention, the transformer version of a monad has the same name followed by a
T . For example, the transformer equivalent of State is
When you need an already existing monad like
IO<A> to obtain the capabilities of a different monad like
Either<L, R>, you use a transformer.
How do we benefit from it?
By using transformers you remove the need for nested types and collapse all of them into a single one that has the same capabilities than all the previous ones by itself.
That means you get rid of the complexity provoked by nested monads, where you are forced to
flatMap over each level to gain access to the next one in the stack. That can become unusable very rapidly on deep stacks.
So your code can become quite simpler thanks to Transformers.
Fixing the stack
Let’s pick the deepest nested types, which would be the following:
We have an
IO computation wrapping a side effect. To gift it with the duality powers of
Either, we can use Either Transformer.
It’s type is:
EitherT<F, L, A>, where
F is the underlying monad type,
L is the left type for the
A is the gonna be the final resulting type.
For our super heroes example it the types would be:
EitherT<IOHK, CharacterError, List<SuperHero>>
Here, you need to understand that
IOHK is the way we use to refer to
IO on a generic type slot. So
IO is here the underlying monad.
It’s part of the boilerplate automatically generated at compile time for all the higher kinds by KΛTEGORY. We will describe higher kinds and their benefits on further posts so let’s obviate it for now.
IO is now capable of retaining errors 🎉. But the stack is a bit bigger:
Reader<GetHeroesContext, IO<Either<CharacterError, List<SuperHero>>>
So we need to gift the powers of
Reader to the already transformed
IO, so it can also provide dependency injection and not just
IO deferred computations and error handling, which is what we have for the time being.
We can use the Reader Transformer to achieve that:
ReaderT<F, D, A>.
F is again the underlying monad being transformed,
D will be the
Reader context with all the dependencies, and
A will be the final result type.
The transformed type requires to be agnostic of what’s
A, since we want it to be able to work over different return types. In the other hand, I am fixing the left type of
CharacterError since I know that any possible error on my system is going to be mapped to one of the domain
CharacterError sealed class implementations. (Check the previous post for more details).
This time, the underlying monad being transformed is going to be:
EitherT<IOHK, CharacterError, List<SuperHero>>. We need to fit that on the
F position of our transformer type
ReaderT<F, D, A>.
That means we must partially apply
EitherT to fix the
CharacterError types, so we just leave
A as generic. Remember that its type is
EitherT<F, L, A>.
ReaderT<EitherTKindPartial<IOHK, CharacterError>, D, A>
And with this we would have our transformed type ready to operate. Even if it can still look like nested types, it is indeed a single one with all the required capabilities.
typealias ReaderT<F, D, A> = Kleisli<F, D, A>
Kleisli<F, D, A> is just a wrapper around the function
D => F<A>. That’s exactly what we have on each one of our computation levels or layers on the sample repo architecture.
You will also notice that I’m aliasing the transformed type to
Result to improve its semantics, since at the end of the day it’s gonna be representing an action result moving across the different layers.
typealias Result<D, A> =
Kleisli<EitherTKindPartial<IOHK, CharacterError>, D, A>
Im also wrapping
Result into a datatype called
AsyncResult that delegates all its functions to the
Kleisli but at the same time provides handy monad instances to compose AsyncResults and use bindings over them. This is going to be really helpful when it comes to the architecture implementation.
So let’s take a look on how the complete architecture of the app is built thanks to the
Architecture using AsyncResult
We are going to start on the outermost layer of Clean, where the frameworks would be. There you would have your data sources and view implementations. Let’s start having a look at the network data source implementation.
We have a collapsed transformed type thanks to
AsyncResult, so we can just wake up a
binding over it, and declare a bunch of sequential operations inside.
Here I am composing an operation by all those sequential ones. I don’t really run the composed operation at all but return it so I can keep composing on top of it thanks to
map and so on. That’s key and it’s enabled thanks to transformers.
The sequential operation blocks inside my binding that will be run when we decide it’s time to run the whole function chain, are:
- Build the super heroes fetching query
- Porvide access to the dependencies by lifting a
Reader(which would be
AsyncResultby itself thanks to transformers) and getting access to the reader context.
- Running the IO computation.
Each one of those operations can optionally call
bind() in the end to force resolution and bind the result to the context of the monad being used, which is an
AsyncResultMonadError instance. That forces the next step to wait for resolution on the previous one so the resulting monad from it is going to be available to compute.
For more details on the
runOnAsyncContext function please take a look at the previous post and its definition at the sample repo. It’s mainly going return an
IO computation that runs the
f lambda code into a coroutine and provides lambdas for both success and error cases.
Then I have a kind of stub repository layer where you would be supposed to implement cache policies and so on.
I wouldn’t personally add this function to the call chain if I don’t have any further logic to add, obviously, but it’s there just for didactic purposes on comparing this approach with an OOP based Clean Architecture one.
You could easily apply operations here to implement the cache policy strategies like flatMapping or mapping over the composed computation returned from the data source.
Please notice that all the functions I am showing here are defined at a package level, I am not playing with useless enclosing instances at all since it’s FP and all the dependencies are passed in as function arguments or provided by the reader. There is no need to have enclosing instances containing this functions, since there is no state to keep on them. IMO, It’s key to understand that. OOP and FP designs are highly different.
The caller for the repo would be the Use Case most likely, where the business logic would be.
I just want to filter out some “non valid heroes” here so I can map over the data source returned computation and do it. Thanks to the fact that we are returning AsyncResults on all levels we can easily compose on top of it since it’s now a single type.
Presentation code could look like this:
I can map or flatMap over the Use Case result to apply mapping and effects, and even handle errors using the
handleErrorsWith function to apply error related effects on the view.
getSuperHeroes() and both side effect causing functions
displayGetHeroesError() would have a return type like the following:
AsyncResult<GetHeroesContext, Unit>, since the moment those get finally unfolded and their effects are run, they would be applying just side effects on the view.
So nothing is returned from an action like that. Therefore the
Unit type. And of course the
GetHeroesContext is just the type we are fixing here for the reader context, where the dependency bindings are defined.
Take a look on both side effect applying functions which are also using the power of bindings to do their work!
I’m mainly using bindings here to get access to the
Reader context where they view contract is defined as a dependency, and then apply effects on the view using it. So if I had to apply many different effects on the view I could just write one after another on this way.
Again a cool point is that all those computations are built on top of the UseCase already composed result. Which means effects are still not run. We keep deferring them until the view implementation decides it’s the right moment to run and unfold the whole call chain, finally performing all the computations and effects!
The view implementation would decide when to finally run the returned composed computation by the presentation layer. Some complexity related to how we are emulating Higher Kinds is still visible there (like the evidence function
ev(), but hopefully KEEP-87 will be eventually successful!. Don’t forget to upvote it :). Otherwise we will just keep the HK and Typeclasses emulation and keep iterating over it to make it easier to use.
What about testing?
Testing becomes trivial when you have a reader in place. In our case a reader transformer. You just need to provide a different context implementation that can be providing mocks for the required dependencies.
So you can keep exercising the real production code base and just pass a different context to assert over the whole system execution. Black box tests are quite ese on this scenario.
So let’s take a rapid look on how I am declaring the Reader context and we will be done. Promised!
This is just a way to build it, where I am kind of mimicking what we are using to build in Android with Dagger scopes per Application and Activity.
In the end it can be a sealed hierarchy that can be implemented in different ways depending on the scope. This approach helps us to define overridable global dependencies like the Android
Context, navigators, api clients or threading implementations. Those would be application scope dependencies most probably.
But all of them are completely overridable by any of the “sub-scopes” declared below. So you can easily add your own sealed class implementation for providing mocks by overriding properties or methods from the sealed class 🎉.
For activity scopes, you can also override context with the current activity one and provide a different instance for the view contract. You can easily provide all this since graphs / components are always created from the view implementation, the same way we do with Dagger.
Don’t forget to step into the sample repo, where a module called monad-transformers is showcasing this approach over a real Android app with a super heroes list and detail activities.
Remember that you can add me on Twitter to know more about this topic and many more. The hash is
And take a look to the previous post of the series if you still didn’t!
“You could touch the sky by building up your own temple, floor by floor, solving problems with mindfulness. Then you…medium.com
In the next post on this series I will write about an advanced FP style that can also be achieved using KΛTEGORY: Tagless Final. You will be able to know how to declare the complete computation tree based on abstract behaviors defined by Typeclasses, and how to provide implementation details later on passing concrete instances for those.
So it’s definitely going to be interesting. Stay tunned!