Dependency Injection in Clojure?

Hi everyone, I would like to ask for your opinion on interfacing in Clojure.

Previously, when working on an OOP language such as Java, my first instinct when dealing with “layered” code is by using interface. Each layer is communicating by interface, and which implementation is used usually determined by which concrete class implementing those said interface is injected. So that my code would pretty much like:

class OuterLayer {
    private InnerInterface innerLayer;

    public OuterLayer(InnerInterface injectedObject) {
        this.innerLayer = injectedObject;
    }

    public int calculateSomething() {
        return this.innerLayer.Calculate();
    } 
}

How does Clojure deals with this kind of needs? I have a logic layer that really doesn’t care which kind of storage that the storage layer use as long as it returns the data that it needs. I’ve managed this far with Clojure (which is not that long) by ignoring the interfacing concept, I just require a namespace that provides functions for storing objects and call its functions. But lately I’ve been thinking that what if I decided to discard those namespaces, would simply requiring different namespace which has exactly the same function names and signatures suffice?

Isn’t that what Protocol does? How would I “inject” and decide which implementation to use? Is Dependency Injection not idiomatic in Clojure?

Your question is about dependency injection, but it’s more about polymorphism. In Java, polymorphism is implemented using inheritance. Another way to refer to this is single dispatch by type. You provide an instance of the interface and which concrete implementation is used doesn’t matter to the caller.

Dependency injection in Java works by reflecting over your interfaces to determine who is dependent on whom. Since a Java signature is the method name, order and type of the arguments, the return type, and annotations (parameter names are normally erased unless you configure the compiler to include them), the DI library can build a dependency graph so it can bring up everything in the correct order, and inject a class’ dependencies upon instantiation or field/property injection.

In Clojure we can only rely on the name and the arity (number of arguments) of the function. The compiler can’t enforce the types, or even that you called with the correct ordering of arguments. We also have more ways to do polymorphism in Clojure:

  • By name: What you’re describing above, require N different namespaces that have the same function names/arities. You can dynamically require the correct namespace based on some condition (config, function param, etc.).
  • Protocols: Most similar to Java interfaces, but protocols only dispatch on type, name, and arity. Existing types can also be extended to support protocols after the fact.
  • Multi-method: Multiple dispatch, using a custom dispatch function. It also supports complex hierarchies that can’t be described in Java’s inheritance structure.
  • Higher order functions: Pass a function as an argument into your function, which does a specific implementation.

Quick intro to Clojure polymorphism: https://8thlight.com/blog/myles-megyesi/2012/04/26/polymorphism-in-clojure.html

Any dependency injection library for Clojure would have to use one of those methods or something else entirely.

I believe these are the most popular libs to achieve dependency injection in Clojure, and they all take a slightly different approach:

Also, I would encourage you to forget most of your Java design patterns when working in Clojure. Here’s some good examples of Clojure versions of popular patterns: http://mishadoff.com/blog/clojure-design-patterns/

2 Likes

Don’t over engineer, only create an abstraction if you need it. Do you currently require to support more then one storage at the same time? If not, just don’t think about it, you probably never will. Most likely you’ll maybe one day want to replace the storage layer, in which case you just refactor the existing functions to use the new storage layer instead.

Yes it was pretty much boiled down to polymorphism I agree. I’ve asked similar question in another communities (clojurians slack) and it seems that the answers there are pretty much said that I need to avoid defining the dependencies in the namespace that use the implementation. Instead I need to pass it as an argument in the namespace functions.

I have been using Clojure for several months now, and I’ve as much as possible tried to forget everything that I’ve learned in OOP. But it seems that old habits die hard. I’m still having trouble to distinguish problem that needs to be solved differently with non OOP language.

You’re right, probably won’t going to need it anyway and it’ll only bring complexity in my code. It’s just because a lot of materials that I read state that one of the Clojure principle seems to be “Programming to Abstraction”. Or maybe I’m understanding the context of abstraction here wrong because of my OOP background.

A lot of good advice here but I don’t think the question of Dependency Injection really got addressed directly? DI/IoC in Java and similar languages is mostly about lifting the burden of object construction away from the developer and handing it off to a framework so that objects can depend on each other in various ways and the DI/IoC framework will figure it all out for you.

In Clojure, there really are no objects, so there’s no dependency graph at that level and no DI to perform. Component is the closest you will find – it will feel fairly OO (indeed, that’s one of the criticisms leveled at it) – but its focus is very narrow: to wire together a graph of subsystems within your application that require a lifecycle: some sort of startup initialization and, potentially, some sort of shutdown cleanup.

Aside from Component, the only dependency graph you’ll have to deal with is namespaces themselves and that is very explicit: a namespace will require its dependencies – the namespaces it needs – and those in turn require the namespaces they need and so on. No DI there.

On the question of Java interfaces and polymorphism, the closest you’ll see in Clojure is protocols and records (which provide “classes” that implement the protocols). But in idiomatic Clojure, you only use polymorphism where you really need it – a la carte polymorphism – and you also have the choice of multimethods to offer polymorphism based on multiple dispatch, rather than single dispatch.

But as a general guiding principle, focus on a separation of data and functionality – something that is really the antithesis of Java – and remember that Clojure is built on certain abstractions already: sequence, associative, counted, indexed, etc. Then use additional abstractions and polymorphism “as needed”.

3 Likes

I’ll plug an old blog post of mine: http://www.rubberducking.com/2016/06/inversion-of-control-ioc-vs-dependency.html

Like @seancorfield said, DI is a framework for encapsulating creation and acquisition logic of objects. It doesn’t make as much sense in Clojure, unless you use it to manage the creation and acquisition of Java objects when dealing with interop heavy code, or mixed Java/Clojure projects.

I think he’s also right in saying we havn’t directly addressed the question of DI. One of the reason why, I think most Clojurist feel DI is an anti-pattern. DI and IoC implies framework, Clojure favors libraries over framework. If you read over my blog post, you’ll see one classification for framework vs library is who is in charge of control flow. Frameworks initialize and call your code, you plug yourself into them. Libraries only help you do things, you call them when you need them.

So, that still doesn’t answer how would you do DI or IoC in Clojure, but hopefully it explains why you are not finding examples or guides about it.

2 Likes

You are right, it’s hard to find many posts about DI in Clojure. One of the pros of DI is the ability to unit test modules in your system by injecting mocks or stubs instances when writing tests.
How you accomplish this in without DI? redefs?

2 Likes

Yes, I use a combination of with-redefs and alter-var-root.

That said, you made me realize something I forgot to mention.

In OOP, the unit is the class. You want to test a class in isolation of other classes. In FP, the unit is the function. You want to test a function in isolation of other functions.

In Clojure, it is recommended that your functions take as an argument the input on which they operate on. This is in effect a form of DI. And it is often the case that a function accepts a function as input, this is a form of IoC.

So now that I think about it, most good Clojure code does heavily use DI and IoC, but it doesn’t use a DI framework.

Let me give a concrete example.

Say you had a DAL module, data access layer to a RDBMS. You would need a DB connection and maybe a JDBC instance to be able to make DB queries. In OOP, you’d have a class called DAL, and its constructor would take a DB connection and a JDBC instance. The class would cache them in local fields, and then all methods would operate over the class fields. So maybe you have a getUser(id), and a getUserByEmail(email) method on the class which uses the DB connection and JDBC instance it got at object construction time.

Now a DI framework would create the DB connection and the JDBC instance and would pass it to the DAL constructor and also create the DAL, and it would then give the DAL to other classes that want to get users.

You would test the DAL class by constructing it with a mocked DB connection and JDBC instance. And then test the call to each method.

What’s important to note is how the DI is at the class level. The methods are not designed in a DI style, only the class is.

In FP now, you would not have a class, so maybe you’d have a DAL namespace. That namespace would have two fns get-user(db-conn, jdbc, id) and get-user-by-email(db-conn, jdbc, email).

Each function is designed for DI. To test them, you just test call each one and pass a mocked db-conn and jdbc instance.

That’s how DI is done most of the time. And designing all your fns to take the inputs it operates on as arguments is the recommended approach.

2 Likes

Hi @didibus ,

Thinking about tests, in which cases would you prefer with-redefs over a function parameter?

Eduardo

For inner functions. If function A calls function B and C you mean use with-redefs to mock B and C while testing A.

There’s times where you’ll do IO without taking in the IO executing code for it. Like say you are using slurp and spit. So you can redef these to mock them.

There’s also always a place where you can no longer inject an argument, like for top level functions. Those normally play an orchestration/coordinating role. For example, the function that creates the db-conn and then passes it to your get-user fn. You’ll probably want to have it use a make-db-conn fn, and in tests you’d redef that make fn so it gives back a mocked db-conn.

Or sometimes you work with code that you can’t change or can’t be bothered to refactor, and a redef is the best option available.

There are quite a few issues with var redefinition (for the purpose of mocking) during tests:

I would recommend dependency passing style as a cleaner approach.

1 Like

And that is true when you don’t have many nesting levels, but in any complex situation it is a rat nest and leads to lot of repetitive code.
This is why tools like redux exists for React for example

1 Like

I blogged about this topic here: https://medium.com/@kumarshantanu/dependency-injection-with-clojure-using-dime-af57b140bd3f

It describes and refers to a DI library called DIME I wrote some time back and updated recently in a new release.

1 Like
(defn calculateSomething[calculate]
  (fn []
    calculate))

(defn logic (calculateSomething 10))

Dependency Rejection by Mark Seemann helped me understand the problem better.
Video
Blog post

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.