In today’s post-OO world, is dependency injection still relevant?

It’s 2015. Most of the new popular languages are more or less functional. The old ones, like Java, gain functional programming elements. In Scala, people are increasingly leaning towards the pure side, using more FP and less OO. So – Dependency Injection? Really?

You could say that DI is just using (constructor) parameters. I’m saying that as well in my talks on no-framework DI in Scala using MacWire. Passing parameters is the basic building block of FP. So why give it another name? Is DI a separate concept?

Different kinds of parameters

Recently I had a short discussion with Alois Cochard on his blog, where he describes an approach to DI similar to mine. He also once wrote a DI library (Sindi), so it’s good to know we came to similar conclusions. But that got me thinking – maybe there are two kinds of parameters?

One kind of parameters would be ordinary “data” parameters. That’s what we pass around functions, manipulate, modify, persist, etc. These are entities, aggregates, collections, case classes. The other kind would be “service” parameters. These are classes which encapsulate business logic, allow communicating with external systems, provide data access.

While you could of course use only FP to create an entire system end-to-end, I think a number of people (me included) find it convenient and readable to use FP “in the small” and objects “in the large”, as described e.g. in the Programming Scala book, or in this Quora question. So you have some possibly pure FP code manipulating your data, implementing the core business logic fragments, and that is encapsulated (organized into?) by objects (you could call them “services”, “modules”, or some name better reflecting the overall responsibility).

That’s where DI comes into the picture. It can be very useful when wiring the “service”/”module” object graph. The means to create the object graph may be the same as when dealing with any other kinds of objects – either by doing DI manually, or with the help of MacWire, or with a container – but the way we are using parameters feels different.

Environments

Another way to think about the “data” and “service” parameter split is that the services form an execution environment for our (pure-FP or not) code. Such an environment may contain services for accessing the database, communicating with the user, etc. Currently we use the same mechanism – parameters – to define the environment (e.g. via constructor parameters), as we use to pass data around functions. But maybe in some future programming language, these concepts will be separate?

Or instead of creating a separate mechanism, which could end up as poorer, “shadow” functions, we could have some more convenient syntax for creating and using environments?

A good example of such an “environmental” parameter is the quite commonly used ExecutionContext in Scala. Currently it is very often passed as an implicit parameter (and it’s contagious, the implicit is needed in every place we want to manipulate futures, from method to method …). At least for me it is a different “kind” of parameter then, let’s say, a Set[Person]. Wouldn’t it be great if we could just assume that our code is executed in an environment, where “some” execution context is provided? Other examples might include “some” way to access a database; or “some” way to talk to a webservice.

Such “environmental” parameters are often what we want to swap out for testing, or change depending on deployment configuration – “canonical” use cases for dependency injection.

Finally, Alois Cochard also pointed me to a talk by Tomas Petricek on a calculus for environmental parameters (it’s just 20 minutes!). Time to study coeffects? :)

  • tksfz

    Others have encoded DI using the Reader monad:

    http://stackoverflow.com/questions/11253653/configuration-data-in-scala-should-i-use-the-reader-monad

    The comments and outgoing links from there are recommended. This is on point: “DI is all about global state, but
    global state (especially mutable global state) leads to brittle, tightly
    coupled systems … Reader provides the convenience of global state … but doesn’t suffer the
    brittleness of global state because the type checker oversees its use.”

    The main complaint with using a monad is the syntactic overhead of boxing your code inside the Reader[_]. This happens in method return type signatures and when using for-comprehensions.

    It would be awesome to investigate better syntaxes for using the Reader monad. For example, there is at least one scala project that uses macros I think to make it easier to use monads:

    https://github.com/pelotom/effectful

    I think a few enhancements to the Reader monad would have to be made to make it work like a full-blown DI system anyway. For example, more natural support for multiple injections, etc. If you were going to spend time on this I’d love to help brainstorm / try out code.

  • Joseph Abrahamson

    This is a pretty common approach in Haskell. You see it either in the “Mother Monad” approach where the entire program is always lifted up into an application specific monad which contains a reader into application state which is built and run during application startup/configuration. You also see it (in the dual form) when using mtl style monad classes to expose “need for configuration”. This is especially powerful when using “classy” style lenses where you’ll see signatures like (MonadReader env m, HasDatabase env, HasLogger env, ...). This code can use type inference to display the exact dependencies it requires.

  • Yes I’m well aware of the Reader monad approach – thanks for bringing it up. The good side of the approach is that it certainly places a clean border on the “service” and “data” parameters, using the terminology from the blog. Why I’m not convinced about is: (1) how does it behave in larger systems in terms of readability, proportion of boilerplate vs normal code and (2) what you mention, the syntax could be probably somewhat nicer. As to (1), maybe it’s my lack of experience and in fact maybe an example “business” project example with DAOs, email sender services etc. would clarify.

    I would argue though that “DI is all about global state”. Now, very often DI containers such as Spring or Weld require or at least promote having a single, global app-wide container and hence global state. However when doing DI manually (as described on http://di-in-scala.github.io), or when using Guice and its modules, there’s no global state. You can easily create multiple independent object graphs.

  • Tom Flaherty

    Even though Scala is strong for FP, its OO with Traits is awesome where Dependency Injection is accomplished with Trait Injection were concrete Traits are injected into abstract Traits during construction with type safety provided by the Scala compiler. I learned this from Jonas Boner’s blog and Programming In Scala by Dean Wampler

  • Robert

    Nice article! Maybe you are right, that we need to separate “environment” parameters from “data” parameters. I would just like to add that there are already two main approaches: In the Java Enterprise world there is the Enterprise Container, which is exactly the supplied “environment”, and in the Functional world there are Monads and Applicators that need to be threaded through. Neither of those are satisfying I think. I think explicit language support may just make the whole thing only worse.

    If you allow a plug, I described a very similar problem (separating “fix” services from instantiation-time data parameters in the constructor) in my own injection library for Java here:
    https://github.com/vanillasource/jaywire/wiki/Propagating-Dependencies#arity

    What do you think about doing just that? Obviously languages that support currying this should be even simpler..

  • Sure, that would work, still requires quite a substantial amount of supporting code (“boilerplate”), so I’m not sure if day-to-day people would want to use that

  • Take a look at *tunnel parameters* in XSLT 2.0

  • Who would have thought, implicit parameters in XSLT :)