

Scrap Your Cake Pattern Boilerplate: Dependency Injection Using the Reader Monad - handler
http://blog.originate.com/blog/2013/10/21/reader-monad-for-dependency-injection/

======
noelwelsh
Clearest blog post I've read on the Reader monad in Scala. This is something I
can see coming to our code base very soon.

The next step for us is monad transformers in Scalaz. We are going to end up
with something like Future[Reader[Writer[Problem \/ Result]]] (a few type
parameters are missing there; hopefully you get the idea). Monad transformers
give a way to flatten this stack of monads into a single monad, which saves a
lot of unnecessary wrapping and unwrapping in code. I haven't yet worked out
the all the details of using them, though. Should probably do a blog post when
I do :-)

~~~
jarhart
I look forward to that blog post. In order to use Scalaz's monad transformers
with scala.concurrent.Future, would I have to write my own monad instance for
it, or is there a way to re-use the one from scalaz.concurrent?

~~~
noelwelsh
The scalaz-contrib library has a Monad instance for scala.concurrent.Future:
[https://github.com/typelevel/scalaz-
contrib/blob/master/scal...](https://github.com/typelevel/scalaz-
contrib/blob/master/scala210/main/scala/Future.scala)

------
kilovoltaire
Great use of the same example under three different dependency injection
mechanisms.

One issue I see with the Reader pattern, however, is that anyone using one of
these injected methods has to deal with the monad stuff of using for
comprehensions to extract the values, et cetera, whereas with the other two
patterns they get to use normal functions.

Put another way: having to distinguish between the "normal" functions in a
class versus the injected Reader functions, and not be able to use them in the
same way, feels like it would get old fast. I wonder if the author has found
this annoying or not.

I do really like how purely functional the Reader approach is though, no
state!

~~~
jarhart
I actually like using comprehensions in Scala so I don't find it annoying at
all. Also I think having a clear distinction between the "normal" and
"injected" functions is a good thing.

I do find having to add implicit parameters to every function slightly
annoying, especially because when I forget, it's not always clear from the
compiler error what I did wrong.

~~~
kilovoltaire
Yeah, I agree the injected functions being "marked" by their types is nice.

------
lmm
It feels like this isn't actually solving the dependency injection problem -
we've moved all our dependencies into this global (ish) Config object, but
that object is effectively acting as a service registry. Constructing the
Config is still going to require a DI strategy (as the end of the post
acknowledges), so what do we actually gain from this reader pattern? I don't
think it makes testing any easier (if anything it makes it harder, since we
have to build up a whole dummy Config, rather than building a small test cake
that only includes the dependencies a specific test needs).

~~~
jarhart
It does actually solve the problem that DI solves in general, because you
create readers that expect the dependency to be injected into them. You still
have to decide how to do the injection and that's true for all of the non-
framework approaches AFAICT. With the cake pattern you still have to create an
object somewhere that mixes in all of the concrete implementations. What you
gain from the reader pattern is being able to limit the biolerplate to edge
where the injection actually occurs.

As far as having to create a whole dummy Config for testing, that's a fair
point, although the reader approach makes it easy to inject it since every
reader is a function of the Config. I usually only have a small number of
dependencies so it hasn't really been an issue for me. If you're mocking the
dependencies each one is only one line of code, and for dependencies you don't
need in the you can just use ???.

------
paf31
I'm not sure I understand the concern regarding the approach using implicits.
Sure, you need to add an extra implicit method parameter to each method that
requires the dependency, but in the reader approach, every same method needs
to be written in the reader monad. In fact, they're really the same concern,
since the reader monad instance is just doing the work of threading what would
have been implicit in the first place.

~~~
paf31
Sorry, now I see it - it enables better type inference. Please ignore :)

~~~
handler
yeah, it is still a similar amount of overhead in typing

~~~
jarhart
Actually it's not. Only the primitive readers have similar "overhead" as with
the implicits approach. Most of the readers are defined by mapping or
flatMapping over the primitives (either explicitly or with comprehensions).
With the implicits approach all of the "injected" methods need to declare the
dependency via the implicit parameter. With the reader approach, only the
primitives have to declare it. Compare the implicits version of UserInfo to
the reader version. UserRepository doesn't appear anywhere in the signatures
of the reader version.

