7 ms·
One of the things I really love about Go is that Dependency Injection isn't idiomatic. You just pass in your dependencies, and I find that immensely easier to w
by lordofgibbons 5y ago
One of the things I really love about Go is that Dependency Injection isn't idiomatic. You just pass in your dependencies, and I find that immensely easier to wrap my mind around when starting work on an existing project.
- eru 5y agoGo is not the only one. When I asked a few years ago, if someone could explain dependency injection in terms that I understand, ie Haskell terms, I got the reply of 'hmm, maybe it's like giving arguments to your function?'.
- emmab 5y agoHaskell has implicit parameters[1]. [1]: https://www.haskell.org/hugs/pages/users_guide/implicit-parameters.html https://www.haskell.org/hugs/pages/users_guide/implicit-para...
- Athas 5y agoWhile this is true, it should be noted that: * Implicit parameters are a language extension, not part of standard Haskell (although extensions are so ubiquitously used that this does not matter much). * Implicit parameters are rarely used[0]. I would not be surprised if most experienced Haskell programmers have never used them, and while they may know they exist, they might not even remember the syntax or semantics. I belong to that category myself. In Haskell, type classes tend to be used for what other languages might do with implicit parameters. [0]: https://gist.github.com/atondwal/ee869b951b5cf9b6653f7deda0b7dbd8 https://gist.github.com/atondwal/ee869b951b5cf9b6653f7deda0b...
- eru 5y agoIn more orthodox Haskell, you can use eg the 'Reader Monad' with a suitable datastructure (or combination of Applicatives) where other languages would use dynamically scoped variables.
- gpderetta 5y agoNice! I was wondering if implicit parameters were equivalent to dynamic scoping. It seems to be the case. more on the topic: http://blog.ezyang.com/2020/08/dynamic-scoping-is-an-effect-implicit-parameters-are-a-coeffect/ http://blog.ezyang.com/2020/08/dynamic-scoping-is-an-effect-...
- jcelerier 5y ago> Go is not the only one. When I asked a few years ago, if someone could explain dependency injection in terms that I understand, ie Haskell terms, I got the reply of 'hmm, maybe it's like giving arguments to your function?'. not sure it's a really good explanation. You aren't the one giving the arguments to the function, it's the framework in which you are opting into which is going to do that. Imagine you are writing a software for, say, rescaling images. You have hard requirements: - You want people to be able to provide their own rescaling code as external plug-ins / DLLs which you do not know about when compiling your host. - You want your host software to provide the memory allocation primitives to the DLLs so that they can allocate memory for the rescaled images in a way that you will have control of in the host software. - The memory allocator being used can itself be a plug-in too, that the user can choose at runtime in e.g. a GUI settings panel. How do you do that ? I'd wager that whatever solution you find, it will really look like DI.
- jppittma 5y agoI’ll hold the rescale image function as a function pointer and only call vía that pointer when I want to rescale. If you give me a new one via a plug-in or dll, I’ll set the value of the rescale function to your function.
- jcelerier 5y agoYes, and you are going to store the function pointers somewhere in your code right ? To show a GUI menu to your users with all the available plugins
- eru 5y agoWell, naively this would sound to me like you would pass these things in as arguments? (Alternatively, for something more Haskell specific, you can also write your stuff generically against a typeclass, and let people provide their own implementations.)
- im3w1l 5y agoThe old school way of doing that is to have a "plugins" folder. Every library there is loaded. It calls a register-library function and provides a struct full of callbacks (for image rescaling or memory allocation in this case). The plugin directly calls memory allocation routines of the host, which delegates to the one selected in the ui.
- LukeShu 5y agoDependency 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")
- agumonkey 5y agoIn FP everything is an argument anyway, but in OO land .. you always have to come up with a new concept to overcome the many limits. That said, I think Scala did that, separating module/package level parameters and functional parameters can help designing/thinking. my 2 cents
- eru 5y agoIn FP, we still come up with lots of new concepts. Especially when it comes to statically typing these 'just an argument anyway' constructs. But yeah, you probably get something like 80% of the benefit of modern functional programming from just first-class functions, algebraic data types (and pattern matching over them), and immutability by default.
- ngc248 5y agoDI is just passing arguments, but there is another term used along with that, which will better clarify why DI is needed, which is IOC (Inversion of Control).
- pjmlp 5y agoOn the contrary, you get DI via kubernetes orchestrations and WebAPIs.