6 ms·
Dependency Injection is just passing arguments. If you're writing a library that does things that involve talking to a database: - Not DI: You `import Databas
by LukeShu 5y ago
Dependency Injection is just passing arguments.
If you're writing a library that does things that involve talking to a database:
- Not DI: You `import Database.PostgreSQL.Simple` and your function(s) take the appropriate arguments to pass to postgresql-simple.
- DI: Rather than you `import`ing a library to talk to a database, you take an argument that is an object/function (a partially-applied `connectPostgreSQL`? an instance of a typeclass?) that you use to talk to the database.
(IDK if the various SQL libraries for Haskell are compatible enough with each other to make that example particularly realistic)
Instead of using a library, you take an implementation as an argument. That's all DI is.
- deleted 5y ago[deleted]
- nerdponx 5y agoRight, I think functional programmers like to trivialize the actual usefulness and value of "dependency injection" as a standalone concept. Here's a Python example without DI: class MyClient: def __init__(self): self.http_client = httpx.Client() And the DI version, which is a small change with big consequences (especially for testing; no more patching!): class MyClient: def __init__(self, http_client=None): self.http_client = self.http_client or httpx.Client()
- Sohcahtoa82 5y agoself.http_client = self.http_client or httpx.Client() should be: self.http_client = http_client or httpx.Client() But yeah, once I learned that DI can be summed up into what you put here, I realized that DI is a $1,000 term for a $5 concept that has $100,000 ramifications in terms of what it enables you to do.
- Spivak 5y agoPython also has a really nice pattern using classes for this. I've heard it called the supply chain pattern before. class MyClient(HTTPClient): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) def do_some_networking(self, arg): super().http_get("server", arg) class MyClientWithDI(MyClient, MyHTTPClient): pass client = MyClientWithDI() # Will call MyHTTPClient's http_get client.do_some_networking("/status")
- nerdponx 5y agoI've actually never seen this one before, and I'm not sure if I like it. I generally tend to favor "composition over inheritance", and I'm not sure what the advantage of inheritance is in this case, compared to standard DI.
- eru 5y agoPython's threads used to work like that, for example. (They still have that as an option, but I think the composition is more preferred these days?)
- nerdponx 5y agoThe old-school way to use Thread is to subclass it and override some methods. The post here is describing a setup where you don't override methods, but instead provide some kind of mixin. I've never seen anyone write this, for example: class MyThreadImpl: def run(self): do_stuff() class MyThread(Thread, MyThreadImpl): pass
- eru 5y ago> Right, I think functional programmers like to trivialize the actual usefulness and value of "dependency injection" as a standalone concept. Not sure? Eg Haskell typeclasses (and that includes monads!) could mostly be replaced by just passing arguments around (basically a bit like a vtable in C++). But it would make the ergonomics almost unusable. Of course, many real world functional programmers (or wanabe functional programmers) like to make fun of OOP.
- gpderetta 5y agoand passing a factory is just call-by-name/call-by-need.
- eru 5y agoWell, factories are really just functions.