You would expect that Haskell, since it took the vow of purity, is excellent at functional programming, and correspondingly poor at imperative programming.
Until she, Haskell, comes back home and you say I know what you have been doing. She looks a bit panicky. The muted TV, the only light in the room, is making the sweat glimmer on your face. You have a glass of whisky in your hand. Too little left—you could hurl it at her.
I know you have been living among them.
And you hurl it. You useless … She bolts off out of the house. Come back here!
What exactly went wrong?
Haskell did not respect the caste system, apparently. It was supposed to be a purely-functional programming language—and, in her defence, she is still pure as pure can get—but she learnt too well the language of the “lower” castes. She speaks it better than them. This is what you overheard, and you are over-reacting.
You see, Haskell is the best programming language for IO. IO is impure, but—wonder of wonders—the best language at it is a purely functional programming language. You prove me wrong: show me a language that treats IO actions as values, and I will recant. See this, which I do in GHCi:
let actions = [putStrLn x | x <- ["cat", "eat", "dog"]] :t actions -- Says it is a list of IO actions. A list of printings. let snoitca = reverse actions :t snoitca -- Says it is a list of IO actions. A list of printings. :m + Control.Monad sequence_ actions -- prints: cat eat dog sequence_ snoitca -- prints: dog eat cat
When next your programming language lets you line up IO actions and count them, reverse them—store them!—and then execute them when the fancy takes you, I will recant. Threads are hard? No, no: programming like it is 1986 is hard.
But how do you explain this to your child? That the best language for fondling IO is the language that is too pure to touch IO? Functional programming has known deep embarrassments, but none quite this bad.