News for hackers. Stuff that matters.
Re:Dependency injection is dynamic scoping in disguise by denisw on Thursday October 31, 2019 @09:28AM (#21408460)
I really enjoyed the comparison of dependency injection with dynamic scoping, and the explanation of how the latter can take over the uses cases of the former with less boilerplate.

But one benefit of dependency injection unacknowledged in this article is that dependency injection is the explicitness of dependencies: the need to pass them in forces the caller to be aware of which dependencies exist, and changes in dependencies cannot be ignored (they lead to compilation errors in statically typed languages, at least).

Managing dependencies with dynamic variables, on the other hand, is implicit. It's impossible to know which parts of the dynamic environment are used by a module without inspecting its source code. And changes to the module's dependencies are not noticed by callers, which may lead to cases where tests fail to stub out particular side effects without anyone noticing.

Given this drawback, dependency injection still seems like the better trade-off to me, despite of its higher amount of required boilerplate. Perhaps it is possible to bring some of the explicitness to the dynamic scoping approach, though.

Re:Dependency injection is dynamic scoping in disguise by roywiggins on Thursday October 31, 2019 @10:12AM (#21408986)
Now I'm flashing back to a system that passed important configuration via globals, so to call functions that relied on these globals, you had to carefully make sure that the global environment was in the right state before you called certain functions.

It was awful.

Re:Dependency injection is dynamic scoping in disguise by matheusmoreira on Thursday October 31, 2019 @02:51PM (#21412284)
Locales in C are an example of this.

https://github.com/mpv-player/mpv/commit/1e70e82baa9193f6f02...

> The locale (via setlocale()) is global state, and global state is not a reasonable way to do anything.

> It will break libraries, or well modularized code.

> (The latter would be forced to strictly guard all entrypoints set set/restore locales, assuming a single threaded world.)

> On top of that, setting a locale randomly changes the semantics of a bunch of standard functions.

> If a function respects locale, you suddenly can't rely on it to behave the same on all systems.

Re:Dependency injection is dynamic scoping in disguise by jetcata on Thursday October 31, 2019 @10:50AM (#21409431)
I’ve worked on similar systems, never again. God objects are something that should be avoided so that code is readable and maintainable.
Re:Dependency injection is dynamic scoping in disguise by roywiggins on Thursday October 31, 2019 @11:57AM (#21410149)
One common pattern in this system was that you'd first save the current environment, run the special "environment preparation" function, then the real function, and then write back the saved environment over the modified one, so that you didn't leave the environment changed after you returned. Unless of course you meant to change it. This was sometimes documented, you'd write in which globals a function expected and which it modified into a docstring.

This was probably only in the top ten of the problems that this thing had, but I do remember it vividly. Making any change was like pulling out a Jenga block and replacing it without toppling the tower.

Re:Dependency injection is dynamic scoping in disguise by eru on Friday November 01, 2019 @06:16AM (#21417412)
> One common pattern in this system was that you'd first save the current environment, run the special "environment preparation" function, then the real function, and then write back the saved environment over the modified one, so that you didn't leave the environment changed after you returned.

That sounds exactly like dynamic scoping.

Re:Dependency injection is dynamic scoping in disguise by mettamage on Thursday October 31, 2019 @10:22AM (#21409101)
Sounds like OpenGL or HTML canvas to me, if I'd had to make a contemporary comparison with what I know.

Fun times, having bugs with OpenGL because of this. Oh, fun times :-D

Re:Dependency injection is dynamic scoping in disguise by mrfredward on Thursday October 31, 2019 @12:54PM (#21410886)
Oh man. It's not as bad now with shaders, but I remember how horrible learning fixed pipeline OpenGL was. You'd try to write some simple code, the result would be a black screen, and you'd just keep adding glEnable/glDisable calls to your code over and over until you figure what invisible piece of global state was ruining your day.
Re:Dependency injection is dynamic scoping in disguise by mettamage on Thursday October 31, 2019 @01:25PM (#21411304)
Oh man, I'm not sure if I'm imagining it, but that sounds like me in 2012.
Re:Dependency injection is dynamic scoping in disguise by all2 on Thursday October 31, 2019 @10:28AM (#21409167)
This sounds like a system we use at work. Go take a look at Tera Term.

In the realm of programming languages it... works. It gets the job done for simple tasks. If you want to do anything complex, watch out.

Re:Dependency injection is dynamic scoping in disguise by code_duck on Thursday October 31, 2019 @12:41PM (#21410711)
Aha, I see you're familiar with my first programs in PHP 4.
Re:Dependency injection is dynamic scoping in disguise by closed on Thursday October 31, 2019 @12:30PM (#21410580)
This reminds me of a lot of Matlab code I had to read in grad school :/.
Re:Dependency injection is dynamic scoping in disguise by whack on Thursday October 31, 2019 @12:47PM (#21410783)
> But one benefit of dependency injection unacknowledged in this article is that dependency injection is the explicitness of dependencies: the need to pass them in forces the caller to be aware of which dependencies exist, and changes in dependencies cannot be ignored (they lead to compilation errors in statically typed languages, at least).

This no longer appears to be true, as most projects nowadays do dependency injection via frameworks like guice or spring. Instead of the caller injecting all dependencies, the caller simply tells the framework that it wants an instance of FooBar, and relies on the framework to magically retrieve all dependencies and use them to construct the instance of FooBar

Re:Dependency injection is dynamic scoping in disguise by DaiPlusPlus on Thursday October 31, 2019 @06:30PM (#21414451)
In production, yes - but in most unit and integration test code projects you still provide explicit constructor parameters.

Many frameworks also provide for dependency validation that can be performed when the program first starts-up - you can then extend your build process to execute this validation code as part of a CI/CD process - so while it’s not strictly speaking compiler-enforced correctness it’s still better than getting a nasty surprise in production.

Aside: I‘d love to see a T4-based static DI object factory for my .NET projects which would be the best of both worlds: full static compiler-enforced dependency correctness without needing to code it by hand. It should be possible to make using EnvDTE or Roslyn. Would anyone be interested in that?

Re:Dependency injection is dynamic scoping in disguise by samus on Friday November 01, 2019 @02:39AM (#21416665)
The framework will fail to do so on startup though - thankfully, Spring defaults to eager initialization of all beans. But yes, you're in deep water when you make beans lazy or if you have no other choice than to make most things lazy for performance reasons, like in PHP.
Re:Dependency injection is dynamic scoping in disguise by _bxg1 on Thursday October 31, 2019 @09:36AM (#21408540)
Plus, passing it via function parameters allows you to do things like currying (outside of the constructor case, I suppose)

  const foo = (env) => (arg1, arg2) => {
    ...
  }

  const fooWithEnv = foo(myEnv)
Re:Dependency injection is dynamic scoping in disguise by eru on Friday November 01, 2019 @06:19AM (#21417423)
That's what the Reader Monad does under the hood. See eg https://eli.thegreenplace.net/2018/haskell-functions-as-func...
Re:Dependency injection is dynamic scoping in disguise by glun on Thursday October 31, 2019 @01:55PM (#21411684)
I think this is a very good point, but I also think good code organization can come a long way in addressing it.

In my opinion, a good test suite should contain mostly module level test[1]. You stub out interactions with other modules (if you both read and write to another module you should use a handwritten test double rather than a mock) but leave your own module mostly unchanged. Perhaps you replace some configuration (like changing the DB driver to run against an in-memory database), perhaps you replace the entire peristence layer, but that should be about it.

The points where your module interact with other modules and external systems can be isolated to a single file or package. Sometimes this means just aliasing a function or class, sometimes it means writing a proper facade. This makes it easy to figure out what needs to change when testing.

I also don't think that this is a problem that dependency injection frameworks are helpful in addressing. Looking at constructors is not much better than reading the method implementations -- you still have to look at every file in the codebase. Manual dependency injection with handwritten or generated (ala Dagger) factories does solve the problem completely though.

[1] I think proper unit tests should be reserved for functions that are computational or algorithmic in nature and complicated calculations/algorithms are rare in the domain I'm currently working in, though this would be different in other domains. You'll also always want some real system level tests, but not too many since they're darn slow.

Re:Dependency injection is dynamic scoping in disguise by gwbas1c on Thursday October 31, 2019 @02:48PM (#21412255)
> Given this drawback, dependency injection still seems like the better trade-off to me, despite of its higher amount of required boilerplate. Perhaps it is possible to bring some of the explicitness to the dynamic scoping approach, though.

There's a difference between a program that's industrial-strength versus a toy project.

Any program that's industrial strength will require effort in strengthening, irregardless of the pattern chosen.

(Honestly, I really wish dependency injection was a language feature.)

Re:Dependency injection is dynamic scoping in disguise by eru on Friday November 01, 2019 @06:18AM (#21417419)
Effort in strengthening being required at all is independent of pattern chosen.

Amount of effort very much depends.

Re:Dependency injection is dynamic scoping in disguise by scarmig on Thursday October 31, 2019 @11:12AM (#21409659)
It seems like this could be a language feature. The key would be an explicit declaration of the identifiers that the function expects to be bound in the calling context. So when declaring the function you optionally specify the dynamically scoped arguments (separately from regular arguments) that callers must have in scope. Doing so allows you to use those dynamic variables within the body of the function.
Re:Dependency injection is dynamic scoping in disguise by erik_seaberg on Thursday October 31, 2019 @12:22PM (#21410460)
Scala implicits. It's a curried arg list you can omit, but the compiler checks that you have implicit values of all the needed types in scope. Or you can pass whatever you want explicitly.

https://docs.scala-lang.org/tour/implicit-parameters.html

Re:Dependency injection is dynamic scoping in disguise by mavelikara on Thursday October 31, 2019 @05:08PM (#21413728)
Scala's implicit parameters and Haskell's typeclass instances are this, correct?
Re:Dependency injection is dynamic scoping in disguise by eru on Friday November 01, 2019 @06:20AM (#21417431)
I'm not sure how you'd override the typeclass instance you are going to get easily? (Haskell typically only allows a single instance per type.)

OCaml lets you swap out the equivalent of the typeclass instance. But it's a pain, because there system also means that you always have to specify which instance you want.

Re:Dependency injection is dynamic scoping in disguise by closeparen on Thursday October 31, 2019 @01:19PM (#21411237)
Dependency injection is typically used with a system to automate the wiring and take care of the boilerplate.
Re:Dependency injection is dynamic scoping in disguise by jkoberg on Thursday October 31, 2019 @04:50PM (#21413594)
"passing them in" isn't really dependency injection, it's just taking arguments.