I'm finding it the first useful introduction to the language I've encountered. Like you, my experiences have been less than exemplary, and I'm way too arrogant to accept that I'm either stupid or brain damaged. Every year or so I take a run at Haskell, and this time I think I'm going to get it. I know a dozen-odd languages, including a fairly expert grasp of APL and Mathematica, both of which are a bit odd in their approach to life. Haskell is genuinely weirder than most, and the entire Haskell community's apparent total ignorance of the of the most rudimentary principles of pedagogy make the learning curve unreasonably steep.
The fact that most Haskell tutorials start out with contradictions is an example of this. Pedagogy 101: don't make false claims about the subject in terms of the concepts you can reasonably expect the student to have.
For example, electrical engineers should never introduce the concept of "negative resistance" by starting off talking about "negative resistance". They should first introduce the concept of generalized resistance (dV/dI rather than V/I) and then show how generalized resistance can be negative. To tell a student that there is such a thing as "negative resistance" when you know full well that "resistance" in the student's mind is not generalized but defined by a relation such that it cannot possibly be negative is simply incompetence on the part of the teacher.
By the same token, it doesn't matter one whit that "the language" is side-effect free when "the runtime" implements side effects when the student can reasonably expected to understand "language" to mean "language plus runtime" as it is in every other case. Actually: it does matter, and it should be explained in those terms. Otherwise students will rightly reject the language as some kind of snake-oil gibberish promoted by arrogant hucksters.
No Haskell tutorial should ever make the false claim that "the language" (as understood by any reasonable student) is "purely functional". Students aren't stupid and haven't been brain-damaged by imperative languages. They know that "the language" covers the whole package in ordinary parlance, including the runtime. When we talk about Perl or Python or C++ or FORTRAN that's what we mean. There is absolutely no basis for anyone to ever assume any other understanding on the part of the student.
So tell people "Haskell is implemented in such a way that it can be treated as a purely functional language for the purpose of static analysis even though at runtime there are various clever tricks that still allow it to implement side-effects like IO". That is a truthful statement. "Haskell is a pure functional language and for every function call the return value depends only on its arguments" is false, outside of a tortured and bizarre interpretation of the terminology that has no apparent purpose but to misrepresent reality to outsiders.
There are some pretty cool things about Haskell. The type inference system is powerful, useful and interesting. It's remarkable to see how far a nominally (but not actually) purely functional language can be pushed, and the way various hacks are used to overcome the limitations of purely functional languages is incredibly clever. The creators of Haskell should be celebrating those feats, not hiding behind false claims that the language+runtime is "truly" purely functional when it manifestly is not.
Edit: Concrete example: to be a purely functional language function values must depend only on their arguments. Here is an example of a Haskell function whose value does not depend only on its arguments:
notPurelyFunctional :: Double -> IO Double
notPurelyFunctional x = do
z <- putStrLn "Please enter a number: "
name <- getLine
let y = read name ::Double
return (x+y)
If we do something like: value1 <- notPurelyFunctional 5.0
value2 <- notPurelyFunctional 5.0
value1 == value2
we have no idea what "value1 == value2" will evaluate to, so either:a) The above code is not part of the Haskell language (which would be weird as it all runs just fine under GHC, and "stuff that runs under GHC" is what any ordinary person means by "Haskell")
or
b) Haskell is not a "pure functional language" the way that term is ordinarily understood, which is: "calling the same function multiple times with the same arguments will always produce the same results" (in which case "value1 == value2" must evaluate to "True", which it does not in general when running the above code).