
From dependency injection to dependency rejection - dmit
http://blog.ploeh.dk/2017/01/27/from-dependency-injection-to-dependency-rejection/
======
kornish
This reminds me a lot of Gary Bernhardt's _Boundaries_ talk and the associated
idea of "functional core, imperative shell". For anyone who found this article
interesting, you might also like this:
[https://www.destroyallsoftware.com/talks/boundaries](https://www.destroyallsoftware.com/talks/boundaries)

~~~
charlieflowers
Thanks for sharing that. That's a great link, and I agree, they are talking
about the same thing and reaching pretty much the same conclusion. The talk is
well worth checking out.

------
charlieflowers
Very good article. If you think about it, you will see this pattern in many
places. Simply because it is the outcome of emphasizing pure functions.

For example, it's the heart of the virtual DOM in React. Pure functions create
an entirely new (no mutation) virtual DOM, and then something at the "impure
boundary" applies this to the actual, messy, mutable DOM of the browser.

With a little thought you can probably find several other well-known examples.

------
obstinate
It's interesting -- this is not what I generally end up using DI for. The
point of DI, a lot of the time, is to put seams into the program where you can
test units of a limited size. If you don't have some way of injecting
behavior, you end up having some serious trouble when you have a module that
brings together the behavior of many sub-modules (how do you test it?). That's
something these short code example style articles never seem to capture for
me.

~~~
DenisM
Why not test the whole thing together? That's what it was meant to be - a
whole.

~~~
bbcbasic
1\. Speed of automated tests. 2. Often it is good to test the semantics of a
sub system are solid before integrating it so that edge cases are routed out
and at the very least it's easy to find where a break occurred.

~~~
marvin
3\. You might have external dependencies that are difficult or impossible to
duplicate in your testing environment. Yes, stuff like this exists -- e.g. a
medium-size retail bank will have at least a dozen, typically multiple-dozens
of external contractors, each with their own APIs and testing environments.

~~~
DenisM
This is the only answer that speaks to me. Although I would think most often
you would have such dependencies be API calls over network. It would me more
prudent in such case to create a mock server rather than use DI. But yeah, in
general API from a third party might call for DI. Thanks.

~~~
bbcbasic
> only answer that speaks to me

So what if a dev home brews parser for instance, and you use that to read in
files from a legacy system via ftp. You wouldn't test the parser code on its
own? You test it all end to end?

~~~
DenisM
If it's just a parser you need to test, there is no need for DI - just give
the parser some files to chew on, and test it directly.

If the "parser" has built-in logic to orchestrate remote file retrieval over
FTP then DI is warranted, as per the other part of my previous answer.

------
daxfohl
Two things:

1\. This is overly simplistic. What happens if your IO and logic are by
necessity interleaved? Grab X out of DB, grab Y or Z out of DB depending on
X's value, etc.? The whole thing just reeks of "ideal case".

2\. This is overly complex. All that really needs to be said here is "pull out
your pure code when possible". There's nothing special about F# to enable
that. The logic in "tryAcceptComposition" is just a function calling other
functions; you can do that in C# or even C. The only advantage F# adds here is
the piping syntax, which to me only serves to make the code more obtuse. But I
guess you couldn't write a three-part series about a single "extract pure
function" op.

(This brings up an interesting thought: ReSharper should come up with a way to
let you highlight a function and extract the "obviously pure" tidbits
automatically).

~~~
yawaramin
You may find it interesting to go over a similar conversation I had with the
post author a few days ago
[https://github.com/ploeh/ploeh.github.com/commit/f89c405e92b...](https://github.com/ploeh/ploeh.github.com/commit/f89c405e92b69c4dfcc65c336a0ff4528f50f029#commitcomment-20681176)

~~~
daxfohl
I'll have to learn more about free monads. In general I love F# for doing
domain modeling and logic, but I still find OO-style DI better for organizing
"services". I've followed ploeh and scott wlaschin for some time and all my
attempts to use their DI concepts in my own real-world code have led to code
that's less intelligible than IoC with no tangible benefit. It's not for lack
of trying, and I _think_ not for lack of intelligence. It just never worked
for me.

If free monads could provide something better than standard DI, and (and this
is a big caveat) still retain decent editor integration (autocomplete, go-to-
declaration/implementation), then I'd check it out. But my gut feeling says
that it'll end up being a leaky abstraction that will need undue patching up
just to maintain it.

~~~
willtim
F# uses .NET classes and objects for a module system, so your use of Objects
for "services" is not surprising. An OCaml programmer is much less likely to
miss Objects and DI frameworks, as OCaml has a powerful module system (i.e.
module functors).

Free Monads can reify an effectful computation, giving flexibility on how it
is interpreted. But they are not really a substitute for a good module system.

~~~
daxfohl
Okay I think I get free monads now. They seem pretty awesome, essentially
letting you plug in an interpreter for the function you're going to run. I can
see high-level how this would appear to be a great generic DI option--you set
up an "interpreter" to handle the statements in your function however you
want: for reals, for test, for reals with logging, etc. And automagically
everything gets executed exactly how you want with no additional cruft.

What makes me cautious about the concept though is that e.g. `do_x_and_y()`
would be interpreted differently than `do_x(); do_y()`, even if they were
fundamentally the same. While "so what?" is a perfectly valid response, that
little tidbit just makes me feel like, while FM's are a very cool abstraction
for _something_ , it's not really ideal for DI. It's just something meant for
a different level. The article "The Wrong Abstraction" comes to mind.

~~~
yawaramin
> ... you set up an "interpreter" to handle the statements in your function
> however you want: for reals, for test, for reals with logging, etc.

That is the tip of the iceberg of free monads. Their full power lies in being
able to _combine_ different type sof effects into more powerful, composed
effects. E.g. you want to do IO while also processing probability
distributions using a probability monad. But they can get pretty hairy. See
[https://youtu.be/qaAKRxO21fU](https://youtu.be/qaAKRxO21fU) for the gory
details.

------
edem
The key part about why dependency injection with partial application is not
functional:

> When you inject impure operations into an F# function, that function becomes
> impure as well. Dependency injection makes everything impure, which explains
> why it isn't functional.

------
henriquelimao
It seems that in the end the initial function Post(ReservationRequestDto dto)
has to call the tryAcceptComposition which has its dependencies hard coded
there. So how exactly this solved the issue?

If you pass the dependencies as parameters in the tryAcceptComposition
function, the Post would have to know its dependencies and we would be back to
the initial state.

I would like to know the whole example he showed before using this model to
see how this scale for more than one function.

------
JackMorgan
Nice! An alternative is to try something like SimpleMock [http://deliberate-
software.com/simplemock-unit-test-mocking/](http://deliberate-
software.com/simplemock-unit-test-mocking/) which strikes a balance between
useful and easy to test. Also there's examples in F# [http://deliberate-
software.com/f-number-unit-testing/](http://deliberate-software.com/f-number-
unit-testing/)

------
pmarreck
More evidence that functional programming is a useful
simplification/complexity reduction on a number of older paradigms.

~~~
yawaramin
Functional programming is actually the older paradigm (if you're comparing to
OOP), but I get your point.

