Suppose you have a piece of business logic which uses various high-level interfaces. You don’t want the business logic to care about how the interfaces are implemented, or where to get hold of the implementations.
If you are a functional programmer, you might resort to a free monad, or to some form of effect handlers. If you are an object-oriented programmer, you turn to dependency injection.
So, are these aproaches basically the same in the end?
I see a couple of differences:
- With free monads/effect handlers, the computation cedes more control to the interpreter than it would cede to the dependency injector. Consider errors for example. If you have an interface for database access injected into your object, and you call some operation on it, you can catch any exception the operation throws. With a free monad, you are at the mercy of the interpreter, which may choose to terminate the computation right away, force a retry, etc. (Quote from Reddit user: “DI doesn’t allow controlling the continuation”.)
- With free monads/effect handlers, you can pass around and manipulate at runtime values representing abstract computations not yet tied to any interpreter. This doesn’t seem to be the case with dependency injection. With dependency injection, you must provide the implementations as you construct your enterprise beans; only afterwards you can pass those beans around.
- With free monads/effect handlers, the interfaces required by the business logic are reflected in the type signature. With dependency injection, sometimes you have to stoop down to inspect a bean’s internal attributes.