Agree with the advice about monads. You're going to think monads are weird because the first one you encounter is used to hide a particular detail of how Haskell works in the real world, namely that Haskell can't mutate objects, but that running your program mutates the real world. Ignore this use case, because while interesting, it's a messy implementation detail, not something to model in your own code.
Use monads for bailing out of computations early (Maybe/Either) or passing some state along through a computation (State). When you're comfortable with those concepts, look at ST for a generalization of what IO does. That is something you can actually use to simplify your own programs.
The State monad is what really sold me on the concept. Right before encountering a tutorial that mentioned it, I had just spent some time writing some code in Common Lisp, where I was trying to find a nice abstraction to avoid threading a state variable through absolutely everything. So this came across as "dear god, the abstraction does exist!" Whereas the IO monad at first blush comes across as "lol PLs geeks realized they had to actually do I/O", which is not as compelling, in part because that version comes across as a solution to a self-inflicted problem.
State is exactly the right abstraction for this. When people using other languages come up with an "abstraction" for state, it is always becomes a testing/debugging nightmare. With State, it really is just a parameter to your function, which you're allowed to hide if you feel like it.
Exactly. IO is nearly the worst first Monad to learn, because none of the internals are visible, making it look far too magic. (Only something like Cont would be worse.) The best choice would be something like Writer or State, where you can see exactly what the abstraction hides, why you'd want it, and how it would look if done via explicit state-passing.
Yes, something like WriterT Error is a great "default state" for your functions: they can produce a correct answer, an error, and some logging information. Programmers love punting things like error handling to higher levels of their program; why not do that with logs, too? (And, given a correct implementation of Writer, if you ignore the log messages, they aren't computed. Try that with Java!)
Do not agree! Monads were always this vague weird thing until I started using IO heavily. Once I was comfortable with IO, applying the abstraction to other things was obvious. I've also helped some other people learn Haskell, and being able to just explain how IO works is much easier than trying to explain the Monad abstraction.
Yes, it's easier to explain a specific concrete example rather than an abstract concept. You don't study literature learning about iambic pentameter and then ending your studies; you also read Shakespeare to see how it's applied in real life. But, you also don't learn to read by starting with Shakespeare. You start with something simpler. The IO abstraction is deep and the implementation messy. So start with State or Writer or something, and go from there.
Sure, but IO gives you answers to the main questions that new people have. How do I write hello world? How do read a line from standard input? How can you DO things in a language without side effects? You can't even write programs without IO, and it's really not confusing.
On the other hand, I think avoiding do-syntax makes sense at first.
Use monads for bailing out of computations early (Maybe/Either) or passing some state along through a computation (State). When you're comfortable with those concepts, look at ST for a generalization of what IO does. That is something you can actually use to simplify your own programs.