Monads are a category of data types. Informally, anything that has
a type parameter,
a constructor that takes an element of that type, and
a flatMap method(which has to obey certain laws) is a Monad. Monads can wrap values, pass them between functions as a parametrized type and allows to perform operations on the values without unwrapping it. You can see some good introduction about Monad from this video. In this post I’m gonna discuss about using a special Monad type called
Reader Monad which we can use for dependency injection. On my previous post I have discussed about using
Scala Cake Pattern for dependency injection. We can get rid of third party dependency injection libraries and cake pattern boilerplate by using
Reader Monad. So it called dependency injection without gymnastic. I’m using the
Reader Monad implementation available in
cats-effects library in Scala. All the source code which related to this post available in gitlab. Please clone the repo and continue the post.
I have used
IntelliJ Idea as my IDE to work with Scala applications. I need to create
sbt project and add the
build.sbt dependency file with
Reader Monad implementation)and other dependencies. Following is the
build.sbt dependency file.
I have two repositories
PermissionRepo. These are the main dependencies of the application. There is
Repo case class which wraps these dependencies.
The repository implementations can interact with database or external HTTP API and do the relevant functions. In this demonstration I have built sample implementations of these repos
There is a handlers which use these functions and do user create, user search, permission search operations. These handlers take
Repo as the dependency. I’m gonna discuss two ways to do dependency injection on handlers,
Constructor Injection(or function arguments) and
The default and easy way of doing dependency injection is with
Constructor Injection(or function arguments). Following is the way to do the dependency injection with
Following is the way to invoke these functions from main class. The repository dependencies are created here and pass them to functions as input parameter.
The main concern of the
Constructor Injection method is, the functions required
Repo as its argument(e.g
createUser() required the
Repo as a parameter). We can get rid of this input parameter in the function and are move it to the return type by using
Reader Monad. When dependencies are injected at the top level, they have to be passed around from one function to another, regardless of whether or not any one particular function actually uses them. The
Reader Monad lets us encode dependencies directly into the type, while providing composability(it provides
faltMap like compassable functions).
Following is the way to do the dependency injection with
Reader Monad. It warp functions with
Reader where we can pass the
Repo in run time. The return type of the function would be
Reader[Repo, _]. The
UserPermissionHandlerWithMonad class shows how to use composability with
Reader Monad. It compose
searchPermission functions with
for expression. The normal
Constructor Injection or other methods we cannot do this. That is power of monads.
Following is the way to invoke the monadic function from main class. The repository dependencies are created here and pass them to monadic functions with
run() method(not via function arguments).