The reader monad for dependency injection

319 views Asked by At

I am a dev trying to bend my mind around the typelevel stack (Cats and Cats-Effect)after years of experience with less functional scala.

I'm having a real trouble bending my head around the Reader monad. I can somewhat follow the examples here: https://dev.to/riccardo_cardin/and-monads-for-almost-all-the-reader-monad-1ife , but one point that totally baffles me (as a person from a more OO background) is WHY are we doing all this? It seems to me that in the end, we just end up with a bunch of functions of the type

def myMethod(arg1: A, arg2: B): Reader[MyContext, MyMethodReturnType]
def myMethod2(arg1: A): Reader[MyContext, SomeOtherReturnType]

Which to me sounds like the exact equivalent of the OO concept of a class:

class MyContext(/*class dependencies used by the methods*/) {
    
    def myMethod(arg1: A, arg2: B):  MyMethodReturnType
    def myMethod2(arg1: A): SomeOtherReturnType
}

Quoting the article:

So, Stocks has a dependency upon StockRepository. How can we express such fact in the code? We don't want to use the constructor injection mechanism or anything related to it. We want to stay functional.

So, my question is why? The quote above seems to suggest that "staying functional" is its own reward, but can you give me an example that showcases how not using constructor injection is an advantage?

Thanks!

1

There are 1 answers

1
Daenyth On

So, Stocks has a dependency upon StockRepository. How can we express such fact in the code? We don't want to use the constructor injection mechanism or anything related to it. We want to stay functional.

I'm going to contest the article here. We do want to use constructors. Objects are not anti-FP. Mutable state is anti-FP, but that doesn't mean we can't use objects.

This is not just my opinion - the authors of the libraries in the typelevel ecosystem also tell you to do it this way, both explicitly (seriously, go ask them) and implicitly (observe how almost every typelevel library doesn't use Kleisli at all)

Using Reader makes sense in haskell, not in scala.

I've been working on typelevel ecosystem codebases for over 5 years, and I've almost never seen Reader (aka Kleisli) used for this, and any time I did, it was bad.

Just use function parameters or class constructors. They aren't scary. Class constructors are roughly isomorphic to Kleisli anyway. You even note that in your question, "sounds like the exact equivalent of the OO concept of a class"

Use Kleisli when you want a value whose api is related to composing functions. A good example where Kleisli makes sense is the http4s library.

Using Kleisli instead of class constructors is a great way to make your team hate scala and advocate for using a different language.