[1]: https://www.haskell.org/hugs/pages/users_guide/implicit-para...
* 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/ee869b951b5cf9b6653f7deda0b...
more on the topic:
http://blog.ezyang.com/2020/08/dynamic-scoping-is-an-effect-...
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.(Alternatively, for something more Haskell specific, you can also write your stuff generically against a typeclass, and let people provide their own implementations.)
Congrats on reinventing DI ! Guess what your array of struct reacale_plugin* is called, hint, it starts with DI and ends with container
People invented this because Java and its friends made it so insanely hard to do it that you needed to invent convoluted techniques to accomplish the same task.
here's one: https://stackoverflow.com/questions/5925270/how-to-do-depend...
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.
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() self.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. 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") class MyThreadImpl:
def run(self):
do_stuff()
class MyThread(Thread, MyThreadImpl):
passNot 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.
That said, I think Scala did that, separating module/package level parameters and functional parameters can help designing/thinking.
my 2 cents
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.