3 ms·
That's the thing with powerful abstractions: they apply to so many things that seem unrelated, that it may be difficult to see the connection. But that's where
by chousuke 10y ago
That's the thing with powerful abstractions: they apply to so many things that seem unrelated, that it may be difficult to see the connection. But that's where all the power lies: being able to use a common abstraction across so many different problems gives you extremely powerful means to express generic operations.
Say you wanted to iterate over a list of image files and apply a transformation to them. In haskell, doing that work synchronously and doing it asynchronously can use the same function, just in a different monad, because the definition of bind determines the execution strategy.
The monad abstraction provides a way to encode a dependency between "operation 1" and "operation 2", or inject behaviour between "element 1" and "element 2" when processing a data structure.
The type signature of bind is "m a -> (a -> m b) -> m b". It might look complicated, but it states "given a monadic value with an inner type of a, and a function from a to a monadic value with inner type of b, you will get a monadic value with inner type of b"
This can be used to access data "inside" a type where you might not have direct access, in such a way that bind determines how the access happens so that it can enforce things like "The preceding IO must have been finished" or "The future must be completed" or "The list values must be concatenated" or "You must compose the continuations properly" or "If any operation results in Nothing, the result is Nothing"
Haskell IO is the usual example of this. Since the IO type is opaque, you cannot construct values of IO yourself directly. An IO String (such as getLine) represents an IO operation that results in a String value. Similarly, the "putStr" function (of type String -> IO ()) is a function that takes a string and results in an IO action that "does something and the value does not matter", or in this case, prints the string to the screen.
However, you can't just compose getLine and putStr directly, because the types don't match. You need to get the value "inside" getLine first.
This is what the bind function allows you to do. And it's defined for the IO type, in such a way that IO ordering is enforced. You can't not use bind, because you have no way of accessing the String value inside getLine without it.
I don't know if my explanation helps. The seeming "complexity" of monads arises from the fact that you can apply the same simple abstraction to so many different things, often in seemingly unintuitive ways.