Functional Dependency Injection

Posted on February 27, 2021

Introduction

On the topic of Dependency Injection in scala, there’s a picture that sticks in my mind that I saw from the scalar 2018 conference’s blog post:

Dependency Injection vote from Scalar 2018

As we can see, the winner by a large margin is constructors, and that makes sense from a functional point of view, make everything as simple as possible.

You can see that the other functional option that has some traction is the Reader Monad, and that’s what we’ll be looking at in this post.

So what’s Reader?

scala mdoc type ReaderT[F[_], I, O] = I => F[O]

Using the extremely simplistic defenition, ReaderT is just a function that has some input and creates some output inside an effect.

It has a Monad instance for ReaderT[F[_], I, *] so we can compose these together as long as they all take the same input type.

Maybe we’d write our program with something like:

```scala mdoc:reset import cats.effect.IO import cats.data.ReaderT

case class Config(host: String, port: Int) type MyMonad[O] = ReaderT[IO, Config, O]


So every action in my program takes a `Config` now, which makes sense, but it's not very nice to use:

```scala mdoc
def run() =
  for {
    _ <- ReaderT { (c: Config) => IO { println(s"Starting a server on ${c.host}:${c.port}") } }
    _ <- ReaderT.liftF(IO { println("started") })
  } yield()

1 + 2