A. High level modules should not depend upon low level modules. Both should depend upon abstractions.
B. Abstractions should not depend upon details. Details should depend upon abstractions.
What in the world is that supposed to mean? Basically, the problem Dependency Inversion is trying to solve is the problem of tightly coupled layers. For example, if you're working with a database you might have tables, and stored procedures, and classes to execute the sps, and classes to use the sp classes, and finally a GUI. If you make a change to a table, you frequently have to change every single layer above that, all the way to the GUI... There are many changes for which this is inevitable. But there are some changes for which this could be avoided. Dependency Inversion is a principle for helping to avoid this kinds of water falling changes by isolating the dependencies between your layers.
Here's a picture:
It's "Dependency Inversion" because we've flipped the direction of the arrow from "high to low " to "low to high". The high layer still uses the low layer, but now the high layer is defining what it expects from the low layer, and the low layer is simply fulfilling those expectations. You can completely rewrite the low layer and as long as it still meets the high level's needs the high level need not change.
There's always a down side, so where's the down side here? The downside is that since the interface has been defined at the high level, the low level depends on the high level! What if we want to reuse the low level somewhere else? If this is a statically typed language like C#, we'd have to reference the high level's dll, even though we weren't going to use it, just because it contained the interface definition.
I don't write much in dynamic languages, so I might be mistaken, but in a dynamic language you could accomplish this same pattern without the interface declaration and without the low level depending on the higher level. Less boiler plate + less dependency definition = more flexible code base. As I understand it, that's the main appeal of dynamic languages.
But back in the statically typed world, how do we use dependency inversion AND manage to reuse the lower layer?
No really, I'm actually asking. It's not a rhetorical question. Hopefully you'll respond in the comments.
One possible way would be to make the low layer define it's own interface, then use the Adapter/Proxy design pattern to create a a class that implements the high layer's interface but uses the low layer's interface... This is clearly less than ideal though.