I still don't get what they have to do with i/o in Haskell, but am ok with i/o in Haskell being one of my life's forever unknowable mysteries.
I still don't get what they have to do with i/o in Haskell, but am ok with i/o in Haskell being one of my life's forever unknowable mysteries.
One of my biggest gripes with Rust (and other languages) is lack of a good monad abstraction (and accompanying syntactic sugar). But I also acknowledge that this is a "I have done too much category theory" problem and and_then is probably a more reasonable name to those not familiar with this particular area of maths.
> I still don't get what they have to do with i/o in Haskell, but am ok with i/o in Haskell being one of my life's forever unknowable mysteries.
They don't. Not really, anyway. IO happens to be a monad, but that's not really relevant to it being used as an abstraction around side effects. Haskell being a pure language is at odds with the necessity of some side-effects, such as printing to the console. The solution is to construct a "to-do list of actions" or "blueprint" for desired side-effects. This is an instance of IO. Any such blueprint that is assigned to the name "main" is executed. That's it.
The monad part comes in when you want to construct this blueprint. It so happens that one reasonable way to do this is via flat_map.
I'm happy that Rust has iterators and I don't have to think about what a monad is at all.
So what the "IO monad" in Haskell represents is the general principle across programming languages where you can, say, read a file, then for each line in the file read another file or otherwise do additional I/O -- and this can spiral out in an unbounded fashion.
As developers we can observe the monadic structure of operations everywhere (and generally seek to avoid it! Monads are fundamentally too powerful!) But whether it is worth encoding the general idea of monadic structures into the type system is a separate question. Haskell says yes, most others say no. There are good reasons both ways. (One underappreciated reason is that exposing a hierarchy of typeclasses/traits introduces API stability considerations. When Applicative was inserted in between Functor and Monad in Haskell, the whole ecosystem had to be updated.)