Dependency injection is dynamic scoping in disguise
gustavlundin.com
gustavlundin.com
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.
const foo = (env) => (arg1, arg2) => {
...
}
const fooWithEnv = foo(myEnv)It was awful.
Fun times, having bugs with OpenGL because of this. Oh, fun times :-D
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.
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.
That sounds exactly like dynamic scoping.
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.
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.
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
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?
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.
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.)
Amount of effort very much depends.
It doesn't make sense to stub out dependencies in unit tests (unless you absolutely have to). Stubbing out dependencies is like stubbing out native functions, operators or loops. They are called dependencies for a good reason; because your class depends on them and assumes that they work. Trying to make dependencies substitutable is overengineering and leads to poor design and gives you more work when implementing unit tests.
Dependency injection is particularly bad because it makes it difficult to find the path to the dependency's source code (which is critical for debugging). Hiding the source path of a dependency is a terrible anti-pattern. When it comes to programming, there are few things more horrible than not being able to determine where some buggy piece of logic is located. I cannot imagine any use case where that would be a fair tradeoff.
Any kind of injection of dependencies should be done via an explicit method/constructor parameter. Sometimes it means that the dependency instance has to traverse a few classes in the hierarchy, but that's way better because at least you can unambiguously track where the dependency came from. Also, if the dependency has to traverse A LOT of classes, then you know there is probably something wrong with your architecture (e.g. a dependency imported too high in the hierarchy and used in a deeply nested class can indicate poor separation of concerns and leaky abstraction between components).
Yes! This functional approach is so simple yet so powerful and magicless.
I've recently refactored a small 20k LOC codebase and the first thing I did was to ditch some magical dependency injector in favor of passing dependencies as parameters to functions. Often dependencies are functions themselves making it straightforward to compose and test.
def send_alerts():
for person in everyone:
Mail.send(type="alert", person)
where Mail.send calls an object that was created with Mail.create(env=os.environ["ENV"])to
def send_alerts(mail: Mail):
for person in everyone:
mail.send(type="alert", person)
where send_alerts is called as send_alerts(Mail.create(env=os.environ["ENV"]))
?I use this approach in all my Haskell/Scala code, and it works really nicely. I have never once felt the need for some magicy DI framework. I just pass the dependencies via function arguments. If it becomes too bloated, I group dependencies, or rethink my design.
def send_alerts(services):
for person in everyone:
services.mail.send(type="alert", person)
?I absolutely agree with this[1]. However, DI does not require this. You can do DI without substitution by default, you can still easily do unit testing, and you can make dependencies explicit. To be fair, I had to build my own framework to make this work exactly the way I wanted, but I also wanted a built in actor framework. I don't have examples of how I do testing, or other features I use, but you can get a feel for explicit injection by default from the readme I made: https://github.com/caseymarquis/KC.Actin
I'm sure there's some established DI framework which uses concrete types by default, and substitution when required. If not, it's not rocket science to roll your own.
1. Substitution by default is useful when you're building a library for mass consumption, but libraries typically don't need DI.
I agree.
To me it seems that the problem is globals. In the Java example, it's convenient to think of "the bank" as global, right up to the point where it isn't (need to substitute a test service, or different banks for different regions etc.) DI replaces global variables. "Magic" DI where this is handled invisibly by a framework lets you pretend it's still a global variable, with all the advantages and disadvantages of that.
I too prefer "explicit" DI where the dependencies are passed in. This does tend to lead to constructing a "world" object which carries around all the pseudo-global variables.
Perhaps this would benefit from language support in some way, just as Perl has $_ to allow non-explicit parameter passing.
Scala has implicit parameters, which are declared explicitly in your method signature, but aren't passed explicitly into the method call (so long as exist in scope as an implicit where the call occurs). They can be confusing, but they can also help.
I would go further and always recommend manual DI - that is passing the objects and creating them in code rather than depending on some magic framework to do the job. That makes understanding / analyzing / fixing bugs a lot easier.
I don't like having to use mocks and stubs, and I think that using those everywhere is indeed an anti-pattern.
Having interfaces for stuff that won't really have polymorphism, it's just there for DI or because some guy likes interfaces is just wrong IMO. Interfaces is a layer of misdirection, if you don't have a use for it, don't use it. If eventually a use comes, then you put an interface there, with modern IDEs, there's even short-cuts to extract an interface from a class and do the renamings if you want. So please don't make "interfaces by default" that some DI apologists seem to love.
I would go further and always recommend manual DI - that is passing the objects and creating them in code rather than depending on some magic framework to do the job. That makes understanding / analyzing / fixing bugs a lot easier.
It's actually not that hard / boilerplate-y to do even in Java (which is what we do in our team).
That is so weird. The reason why we have DI and DI frameworks is because we are asked in requirements to make things configurable out-of-code (and at runtime), because someone wants to use a pretty UI or a config file of sorts to set up which behaviour is used to do $thing.
This is a pretty fundamental aspect of polymorphism and composition.
What I meant about weird was that OP's proposed "solution" to DI is to "do it manually", while the exact reason while we want DI in the first place is not to do it manually (or, in the way that I heard it most in my life, "to be able to change it without rebuilding")
No, the main reason we want DI is to change the behaviors based on what's passed. That's dependency injection. Framework passing the dependencies is automatic dependency injection and what I'm advocating against.
but which behaviours are passed depend on something external to the system - config files, etc. - so you need something that gets the information from somewhere and instantiates the correct class - and trust me, you don't want to write
Protocol proto = null;
if(config == "protocolA")
proto = new ProtocolA;
else if(config == "protocolB")
proto = new ProtocolB;
else if(config == "protocolC")
proto = new ProtocolC;
else if(config == "protocolD")
proto = new ProtocolD;
else
throw whatever;
return new MyObjectWithDependencies(proto);
especially when your system supports >50 protocols, and your object also needs a logger which can itself be of 12 different kinds, a file accessor which can mmap or not according to configuration & os, etc etcThat's a reason, but it's far from the only one. The way I've seen DI used over the years is far more about separating out concerns in the code via interfaces, or making things testable without side effects, than it is making those things dynamically pluggable / controllable via config, not code. The majority of interfaces passed as dependencies into constructors have a single real implementation (outside of unit tests).
I lately err on the side of explicit manual wiring as the cost saving of doing it all automatically does not usually offset the costs of extra magic and lack of compile-time safety that it incurs. Exceptions are where there is a lot of AoP interceptor stuff to wire in, or if I want something to help with lifecycle scoping (e.g. per-request lifetime for a set of related things), at which point a DI framework will likely start paying for itself.
Which is a pragmatic reason, but not a good one at the organizational level. It's every bit as easy to break your system via a config change as via a code change.
Because config is not covered by change control in your case?
Don't ask me why, it sounded crazy to me too.
Which is ironic in regard of DI frameworks that rely on in-code annotations to wire dependencies, especially in compiled languages.
Let's not even get started with languages that do not allow ad-hoc object/value creation from external manifests directly, like Go.
In this article, that's the only kind of injection mentioned. I realize a lot of the times people talk about DI they're referring to magic injectors like Spring's, but in this article, and in the Wikipedia article on DI, DI and magic are not synonyms. When the injector is just the constructor that's still DI, but it isn't an anti-pattern.
These kinds of statement make me question whether you have ever worked with any framework that is not under your control.
The long string of assertions that follow this remark are quite wrong and misses the whole point of unit tests. You are expected to write tests that test a specific unit of software, and that unit alone. You are expected to write tests that provide well defined input parameters to your unit of software, free from side effects, and you are expected to put together combinations of those input parameters that enable you to cover an adequate amount of code paths within your unit of software. By far the best and easiest way to pull this off is by mocking interfaces.
When you start testing multiple units of software interacting or writing tests that rely on specific behaviors of external units of software them you're already testing one level above what you're supposed to be testing in the test pyramid: integration tests.
I try for sociable unit tests(it's a thing), which classes that are expected to work together 100% of the time are tested together. Mock out the external dependencies(http etc) or things that are expected to change.
I tend to only mock out/simulate primary ports(inputs, such http requests), and secondary ports(databases calls) for simple web apps.
I may start isolating stuff for testing purposes when it can be used well independently such a piece of middleware, or things that can be used by multiple parts of a project.
The main difference socaiable unit tests, and integration tests is that traditional integration tests still talk to the db.
There are occasions when I'll mock objects, but it's rare. Working in gamedev, maybe I'm just in an environment where the possible perf/isolation benifits of mocking for the purpouses of isolating a unit just aren't as pronounced?
There are still situations where dependency injection is a wonderful decoupling tool, but I can't even remember the last time I used it exclusively for the purpouses of mocking.
Furthermore, unit tests don't really test product use cases.
Personally, I find testing a single class with mocking best for edge cases, error conditions, or when a class is something like a custom collection that has little outside dependencies.
Unit tests are supposed to verify a software unit's design contract. If a minor change breaks your design contract then either the change is not minor at all or you have more serious problems to deal with than writing unit tests.
If you have a car and you want to write a unit test for the wheel, does it make sense to stub out the tire? Or if you have a door, should you stub out the door handle in your unit test? I would say no because a good test is still about the functionality of the unit/component.
Testing higher level components doesn't stop you from also writing tests for the tire or for the door handle. If the higher level components have a well abstracted interface, then you should be able to substitute any child component with any other working component later and the higher level component tests should still work without any changes required. You can even change the interface of children/sub-components; doesn't matter, the parent component tests keep working. Note that this is not true if you stub out the children, you need to keep updating the stubs.
First, I don’t know how to accomplish this without mocking / replacing the email dependency before testing, even in an integration style test. Emails cost and how would I even verify if I did send the email. Second, I really am failing to see how this is poor design and am genuinely curious.
I can think of so many scenarios where some “orchestrating” service uses multiple single unit services to pull off some task. To me, mocking is incredibly useful for ensuring that you test a lot of failure cases in these scenarios by mocking to force an exception.
A lot can be accomplished via integration tests rather than mocking (actually using a database for example). But to somehow suggest mocking is across the board bad seems like an illogical position
This is one of the neat things about Java 8 Functions. I'm not sure about what their design decisions were but effectively they backed into coding to interface vs. implementation, which used to be a solid design principle.
Mocking makes it quick, easy, straightforward, and cuts out a ton of code noise from the test.
No code changes between dev, test, and production, but I added an environment variable to represent the e-mail host so that I can change where e-mails are sent as needed.
I dislike mocking, so I took the time to figure that out and set it up. If mocking works for you, I likely wouldn't go out of my way to change things to get rid of it.
The bulk of the testing in this design would be easy to write unit tests around the 'algorithm' functions. The coordinators have almost zero logic in them and so are mostly just there to coordinate components together and generally demand less unit testing. Since the coordinators coordinate actions and algorithms then they'd likely have dependencies (the actions in this case, generally) passed in using constructor injection or parameters to a function.
In tests, you will usually inject a mock of any dependencies that you don't want to test, in order to isolate the unit you do want to test.
Slow tests are tests that aren’t run. Having unit tests that depend on databases and APIs are slower, increase the deployment cycle and usually end up being skipped.
Trying to make dependencies substitutable is overengineering and leads to poor design and gives you more work when implementing unit tests.
How is it a unit test if it can fail due to other dependencies?
Hiding the source path of a dependency is a terrible anti-pattern. When it comes to programming, there are few things more horrible than not being able to determine where some buggy piece of logic is located. I cannot imagine any use case where that would be a fair tradeoff.
If you are only using dependency injection to enable unit testing, finding the real implementation is trivial - it’s the only concrete implementation of interface to the constructor.
Any kind of injection of dependencies should be done via an explicit method/constructor parameter. Sometimes it means that the dependency instance has to traverse a few classes in the hierarchy, but that's way better because at least you can unambiguously track where the dependency came from.
Again if you have only one concrete implementation and you’re using a DI framework, finding the implementation of an interface is trivial with any halfway decent IDE.
If you want to run a full suite of integration tests, be my guest, but none of those will ever be unit tests.
Can you expand? Not sure I understand. Is it more difficult because the dependency is separate by an abstraction layer? Imo if DI is done right, the dependencies are always explicitly injected so there shouldn't really be any confusion about what the 'source' is.
If many injections are possible, then I'm in a debugger breakpoint in the code where it's used, I don't have access to the call stack of how, where and why the object was injected with that dependency.
If it's usually just "the one" dependency except when testing, then if I'm looking at the code of a particular function, I don't have an easy way to see the code that must always get executed there because it "could" be (but isn't) anything.
In Java, using frameworks for this is common mainly because it has reflection and annotations, which means you don't actually have to manually call a lot of things to get your objects. E.g. in Spring, all you need to do is slap the annotation @Component on your component classes and it sort of self assembles the object graph from just that information. It's kind of neat if you do this properly. There are tons of ways to customise that or do it differently but it can be pretty minimalistic.
You can do this without frameworks as well (lookup DIY dependency injection). It basically just means that you write the code that constructs your objects yourself and do the right thing of not putting that in the wrong place (which results in hard to test code).
There are only two simple rules you need to remember: constructors must not do work (like constructing other objects or initializing some middleware) and all dependencies come in as constructor arguments. If you do that consistently, you are doing DIY dependency injection. Any time that looks tedious, you are likely violating some of the SOLID principles (e.g. because your constructor has 10 dependencies and the whole class suffers from poor cohesiveness).
In Kotlin, the trend is to move away from using reflection or annotation towards using more explicit DSLs with helper functions that figure out how to construct stuff at compile time (by using the type system and some language features). For example, KOIN https://github.com/InsertKoinIO/koin is a framework for Kotlin that uses neither reflection nor annotations. The same principles are used in Kofu, which is a kotlin centric way of doing similar things for Spring. Aside from being easier to debug (both of these use simple function calls), it also has the side effect of enabling better startup performance as well as native compilation using e.g. Graal.
Whichever way you do this, IOC ready components are easy to unit test because nothing gets constructed as a side effect of constructing them. Side effect free construction might be a better term. I was recently working on some python code that was the opposite where db initialization happened as a side effect of importing some module that exposed a global variable. I would call that broken by design. Python has all the tools you need to do better than that. There's no technical need to write broken code like that. I've seen similar problems in javascript code bases for frontend as well as node.js. Usually if you find a system that is hard to unit test, this is the root cause.
That was what confused me about IoC/DI when I first started hearing the term: it seems so fundamental that it had never occurred to me that there was any _other_ way to write software. I was surprised that anybody was surprised by this pattern.
The second sentence seems to invalidate the first (and I agree with the second but not the first). If you're injecting dependencies using language features, you're still doing dependency injection - and doing it a lot better than the useless Spring "framework".
This doesn't really make sense when it comes to web services and distributed systems. Often you can mirror a service dependency with a code dependency--whether that code dependency is a proper client library or just an ad-hoc client module that encapsulates the particular service calls you have to make--and then inject that dependency. It's really valuable to be able to stub those out if you don't want your unit tests making actual service calls.
> Dependency injection is particularly bad because it makes it difficult to find the path to the dependency's source code (which is critical for debugging).
...
> Any kind of injection of dependencies should be done via an explicit method/constructor parameter.
In my relatively limited experience with Spring, explicit constructor parameters are the idiomatic mechanism, combined with annotations. And when you follow this idiom, it's actually pretty easy to trace back to the dependency in IntelliJ.
My only complaint is that when I went to go read through the other articles on your blog and perhaps add it to my RSS feed there weren't any other articles! This wasn't the first thing you have written... is there a place I can read some of the others?
These mechanisms are addressing functional requirements in component oriented systems, but in the industry have been misunderstood and misused to satisfy testing requirements.
And if one is not doing pervasive component reuse across multiple systems and projects, the on-off usage of DI is of course completely over-engineered and likely a poor design decision.
Can you make it so your site uses HTTPS?
Can you make it so it doesn't just load an frame pointing to Github.io?
Both combined seem very sketchy to me.
Not sure how well that works for other people, but I think the way you wrote it makes them understandable even for eg people who don't really know Java.
For example, factories work around the new keyword. The new keyword in Java emits the constructed type into the bytecode, making it a hard ABI dependency. So people invented factories: they hide the new keyword behind methods that return interfaces. In better languages such as Smalltalk, new is just a method that can be overridden.
Singletons work around the fact only objects can implement interfaces. Classes are natural singletons and yet they are second class citizens of most languages. It is not possible to pass the class itself to code expecting some interface. So people are forced to create an object and add complicated boilerplate code to prevent more than one instance from ever being created. In better languages, classes are the same as objects, they can conform to interfaces and be passed around normally.
Classes can simply override that method. The default implementation of new is:
^ self basicNew initialize
And basicNew is a method that allocates and returns an instance of the receiver.So a custom implementation can easily change self to some other class, chain more messages or replace initialize with something else, add more logic before an object is returned and so on.
> And wouldn't that just make the "new" method a factory?
Smalltalk actually predates the discovery of object-oriented design patterns by a couple of decades so it's the factory methods that are like the new method. For some reason language designers turned it into a magic keyword and people rediscovered the fact that methods are better.
In Java, an interface could have a static method that returns concrete implementations of itself.
`Bubbles class >> #newGum` ^ GumBubbles new
`Bubbles class >> #newSoap` ^ SoapBubbles new
The difference here is that the base class still serves as a true base: it will have all the common functionality for various kinds of bubbles
I don't think it is controversial to say those languages are better. A language with limitations that must be constantly worked around must be worse than a language without those problems.
The ABI is also important for performance! You aren’t making this comparison in good faith. You can easily acknowledge the entire trade off rather than just calling one better.
http://journal.stuffwithstuff.com/2010/09/18/futureproofing-...
http://journal.stuffwithstuff.com/2012/12/19/the-impolitenes...
http://journal.stuffwithstuff.com/2010/08/01/class-in-the-fr...
http://journal.stuffwithstuff.com/2010/12/14/the-trouble-wit...
The flaw in Java is that constructors aren't functions, so you need to wrap them. Wrapping is easy in simple cases but gets annoying fast when you have lots of arguments.
To get around this, you can group arguments using a struct, and sometimes there's a natural way to do it, but that gets annoying in languages without lightweight, easy-to-use structs.
If you had a good composable (parameterized) module system, you'd have much less need of DI. A composable module system would scope the lookup of type names and static methods to the actual module arguments arguments the accessing module is constructed with.
The problem with `new X()` vs `@Inject X x` is that the construction of X in the former has no indirection; type names are global constants. A module system provides an indirection. Dynamic scoping could also provide an indirection, because dynamic scoping lets you redefine / redirect those otherwise constant things.
(DI in practice does a bunch more, like proxies to let you put data with different lifetimes (request, session) in a mixed object graph; and the fact that proxies now exist means aspect-oriented programming sticks its head in and encourages its use for things like auth and transactions. Once you go over the edge of the DI barrier to acceptance, "best practices" shift dramatically - you end up quite far from where you started.)
Earlier, they established the idea that this was a bad thing by having the printGreeting() function print a message you probably don't want. The change in the value caused bad behavior.
However, with dependency injection, you should be following the Liskov substitution principle. You might get different values, but they should all be following the same contract.
The acceptOrder() function might get different implementations of BankService, but the difference should be opaque to it. Calling bank.chargeMoney() should work the same regardless of which one you got.
The reason I bring this up is the crux of the argument presented here against dynamic scoping is, "Dynamic scoping makes it hard to figure what our program actually does, without executing it, and that’s not a quality we want our programs to exhibit."
To the extent that you successfully pull off having different subclasses follow the same contract, this weakness doesn't really apply to DI (or inversion of control).
So why not make it a singleton? Or even better, make it a static class with some static properties?
Yes I know, I said something evil! But before you take out the pitchforks, bear with me:
public static class Master
{
public static ISupplyService Supply;
public static IBankService Bank;
public static IMailService Mailer;
}
This is C# and I actually LOVE this pattern. I have seen it referred to as Master-Pattern, but I don't know the correct term. It solves a lot of problems:* You don't have to pass essential modules around anymore. You can initialize the Master once and access them everywhere without caring about their implementation.
* Code completion does wonders on this one. You simply type "Mast..." and it will show you ALL available modules. You don't have to remember anything. It's awesome for new team members.
* It is fast. In release builds you can even try to replace the interfaces with the actual classes implementing it for a straight method call.
It introduces a few problems:
* You increase coupling. Once you add a module, removing it or significantly changing the interface is tricky.
* You need to be careful to NOT couple modules to each other, and if you do, do it rarely. Otherwise you will have to instantiate the modules in a specific order which gets cumbersome.
In my opinion: If you are a small team and have sane teammates, give this one a try. It reduces boilerplate a lot and reduces the amount of arguments you need to pass around everywhere.
This is much harder to do when everything is a static.
Edit: some typos.
Isn't the interface mainly for the compiler, and in runtime it's just a straight method call anyways?
So this has the advantages and disadvantages of that.
So you could use a good DI framework such as SimpleInjector and get all the benefits without all the problems. You only have to initialize once, and then it's injected into constructors when needed, and you can inject different implementations depending on context.
"Second, we can now pass in different implementations of our dependencies when executing in test3. This is very good, but let me rephrase that in more general terms: the values associated with certain names are now dependent on the environment in which we are executing. This should sound very familiar, dependency injection is just a more controlled form of dynamic scoping."
This seems slightly off to me, unless I'm misremembering my java. In the OrderService example, the value of the variable `bank` cannot change because it's a reference: it'll always refer to the same object. However, if the instantiated `BankService` is mutable, then the internals of that object could change in various ways. Hence, in practice, the dangers of this pattern only seem problematic if dependencies have mutable state.
Back when I was doing java, we used spring beans everywhere for this sort of thing and iirc, they had no mutable state. In Python, I use a similar pattern a lot, where I have classes that are in practice 'immutable once initialized' -- though of course, in Python you could always mess around with the internals at runtime -- which are segregated from classes or objects that have mutable state. (Similar to the structure described here: https://medium.com/unbabel/refactoring-a-python-codebase-usi...)
Of course, I get that you can't in practice know how stateful everything in your dependency graph is. But I think the real problem here (if there is one) isn't explicit DI in the form of dependency passing, but (unexpected/hidden) object mutability.
I see [1] you're using <frameset> to wrap GitHub Pages with your own domain. You could do it in a less hacky way by creating a CNAME file in GitHub repo, and updating GitHub repo settings + DNS settings of the domain:
https://help.github.com/en/github/working-with-github-pages/...
(Unless there's some particular way why you don't want it? I'd be curious. Ability to gather server-side logs?)
[1] learnt it because the page fails to load with uMatrix extension so I checked source.
I think maybe a better analogy for dependency injection is imports. When library A imports library B which imports library C, they can just declare that, nobody needs to assemble "new A(new B(new C()))". Dependency injection is the same thing, but instead of libraries you have stateful objects, like "a RequestHandler needs a RequestContext which needs a DatabaseConnection". Maybe these tasks could even be handled by the same tool, but I haven't seen such a tool yet.
https://www.jamesshore.com/Blog/Dependency-Injection-Demysti...
From the article -
The Really Short Version
Dependency injection means giving an object its instance variables. Really. That's it.
I think most code should be purely functional and unit tested that way, which means that the only dependencies are the input parameters. "Mock" dependencies used in unit tests are usually a unit-test circle jerk; most of the time you're essentially testing that your programming language can indeed make method calls through an interface and those tests are only there because you added DI in the first place. It's common to see all kinds of testing like this but nothing testing the actual functionality of the code because that's so obscured by DI or mocked out. It feels like you're implementing comprehensive testing but it's mostly just additional complexity obscuring the fact that you're not actually testing anything real.
The code that can't be functional? Sure, go ahead and knock yourself out with dependency injection and IOC. It's great for not having to pass configuration and logging instances around. But it's being abused when it's all over the place and you can't look at code and figure out what it does without also looking at configuration files and startup classes and knowing how the flavor-of-the-month DI framework works.
The Closure example works because all your foreign symbols are coming from namespaced imports, which is to say you have a Module architecture and your only polymorphism lever is altering the namespace mapping to point to an equivalent module.
With Java and the like you have a Service architecture, so rather than importing symbols you're injecting services. Things that would otherwise be exported as bare functions tend to be written as classes so that they can be used as services.
While some languages can support either (i.e. JS is typically modular but there are things like Bottle if you want to write JS in a service-based way), it's not typical that a given application is going to support either simultaneously. Most languages have a clear idiomatic choice that you're likely not going to want to stray from to avoid headaches integrating 3rd party dependencies and developers other than yourself.
This is not really true. First off, this is too much focus on "dependency injection", which is just one usage of a more general feature: passing arguments to functions.
Is dynamic scoping the same thing as passing arguments to functions? Well, they are certainly closely related; for example, see the "implicit parameters" paper[0]. But I think it is incorrect to say that converting a function to take more arguments is "emulating dynamic scoping", any more than function arguments in general are "emulating dynamic scoping".
[0] https://www.researchgate.net/publication/2808232_Implicit_Pa...
I think there is no real need to dwell over the subject without knowing particular context
with patch('requests.get', fake.get):
definition = find_definition('testword')
[1] https://docs.python.org/3/library/unittest.mock.html(define foo (make-parameter "some value you want by default"))
(define (do-foo) (do-something (foo))
(do-foo) ; uses the regular foo
(parameterize ([foo "my injected foo]) (do-foo))
And it all just works as you would expect. `foo` gets automatically reset to the prior value when it leaves the scope of the current parameterize block. You can also nest them safely, or use them in macros, and IIRC even inside threads.
bash isn't dynamically scoped by default; it has only global scope by default. You have to use "local" to dynamically scope a variable binding.
When I assign in bash without "local", it affects global scope. There is no way to do the corresponding behavior of "setq" in bash.
#!/bin/bash
v=xyz
f1()
{
printf "v = %s\n" $v
v=clobber
}
f2()
{
local v="abc"
f1
f1
}
f2
f1
f1
Output: v = abc
v = clobber
v = xyz
v = clobber
Almost line-for-line analogous Common Lisp program. Defining the variable is mapped to defparameter, local mapped to let and setq to assignment: (defparameter v 'xyz)
(defun f1 ()
(format t "v = ~a~%" v)
(setq v 'clobber))
(defun f2 ()
(let ((v 'abc))
(f1)
(f1)))
(f2)
(f1)
(f1)
Output (from clisp): v = ABC
v = CLOBBER
v = XYZ
v = CLOBBER