I've been writing Haskell code for years, and this is one of the most singularly useful articles I've read on it. I've certainly found the proliferation of "monad tutorials" and "learn this so you can print something" guides obnoxious, but it never occurred to me that IO really could be taught completely separately from monads. Here's the outline of what the middle of a tutorial could look like, following this advice…
"Here's how to read something and then print that: >>=". Sure, like `getLine >>= putStrLn`. Technically speaking, that's correct. But this doesn't generalize at all.
Maybe I want to greet the user by name. How do I read the name, prepend "Hello, " to it, and then print that? This is a totally natural thing to want to do, and is very straightforward in Java. But our Haskell newbie hits a wall - none of your cases cover it!
Here's how I might write that today, without using do notation: `getLine >>= (return . (++) "Hello, ") >>= putStrLn`. Look at that - it's totally nuts, and I would not attempt to explain that to a Haskell neophyte.
Using do-notation, you can make things nicer:
do
name
but this introduces a bunch of new syntax to learn: the difference between let-in and let and I don't have a better approach than what you suggest. I suspect that this stuff simply can't be made easy.