The Inversion of Control (IoC) design pattern, functional programming, and Domain Specific Languages are all different faces of the same underlying concept and can be used interchangeably. Recognizing this should make it easier to understand each one and when they should be used within your own code.
IoC simply refers to a library passing control to it’s caller. A typical object-oriented implementation would be a function which takes an object that implements ISomething where ISomething has a single method that the function then calls. Those familiar with the visitor pattern should recognize that the visitor pattern is just a specialization of IoC where the callers code is applied to each object in a collection (actually any structure, it doesn’t *have* to be a collection).
I think the tie between IoC and functional programming should be pretty obvious at this point. If your language supports first class functions and closures, then creating an interface with a single method is just extra work when you can simply pass a function as a parameter. In fact, since a closure will close over lexical variables to maintain their “state”, both types of IoC implementation (Interfaces and Functions) are exactly equivelant in terms of power. I believe that using a simple function is more readable though, and it’s certainly more convienent for the caller to not have to create a class that implements ISomething.
The tie between DSLs and IoC is more abstract and requires exploration into the problems being solved. IoC is a useful design when the problem is not completely specified. If you are writing a library and the only thing that will vary in how it’s used is the inputs, IoC is just extra typing with no real benefit. If your problem is only partially specified on the other hand, then IoC allows you to implement the bits of the solution that you can infer from the specs, and leave the rest up to the caller. This is why very generic libraries tend to use IoC a lot. A DSL typically provides the users with a base set of combinators (functions that create functions out of other functions) to build up functions that perform a specific type of task in a very high level manner. A DSL for writing parsers should look very close to BNF so that a grammar can be encoded in the host language with as few implementation specific details as possible. How to do this with combinators is an interesting topic itself that deserves it’s own post and maybe I will get motivated and write one some day. A more intuitive example is configuration files, any time you have a configuration file that can change the flow of a program you are creating a mini-language and you are passing control to the end-user (ie.. IoC).
So great, they are the same thing conceptually, what do we gain from this? Well we can transfer the knowledge we have from one paradigm (like OO) to another (like functional) and vice versa. More concretely, by recognizing that they are all ways to partially specify a solution, we now have a good solid concrete criteria for deciding when one of these types of implementation are necessary.
On a side note, this should help highlight the difference between a pattern and an implementation. Interfaces/Functions/Monads are ways to implement the IoC pattern. Creating a library to “handle IoC” is confusing the two, and if you find yourself writing or consuming one of these libraries, then some reflection on how your handling the task at hand would probably do some good.