And that isn't enough? 'Use IO only where you must' is not second-class? In any other context, I would definitely see that as second-classness - 'Oh, I only employ black workers when I simply can't get any white workers and must employ them'. IO is first-class only in the restricted and technical sense that yes, the type system will let you pass around IO values and functions.
> I disagree; ST would be practically useless if it could not be relied upon to use mutation.
But it still acts immutable and for all we know, is immutable. Just because some compilers and libraries use mutation doesn't prove your thesis that Haskell is as mutation happy as something like Java. As Isaac Asimov said, the man who thinks the Earth is a perfect sphere is wrong, like the man who thought it flat, but if you think he was just as wrong as the flat-Earther, you're more wrong than them both.
In that case, IO is second class in most languages, including Python, Ruby, C#, Java, C++, and C. I do not consider any of them to be functional.
> But it still acts immutable and for all we know, is immutable. Just because some compilers and libraries use mutation doesn't prove your thesis that Haskell is as mutation happy as something like Java.
No, it doesn't "act immutable"; an ST that acted immutable would be equivalent to State. The only advantage of ST over State is that compilers implement it as a special case of IO, and thus it can be used in place of IO for known-pure mutations.
[citation needed]
I've never noticed any particular dislike for IO in the language or community, excepting cases where it was obviously not appropriate. A general feeling that IO is best minimized is common throughout all languages and experienced communities.
> the whole point of ST is that it's safe for it to be implemented using mutation while still being treated as having immutable semantics, this doesn't mean the compiler or library actually use mutation in ST code.
I disagree; ST would be practically useless if it could not be relied upon to use mutation. Its purpose is to signal "this code uses safe mutation", not "this code is immutable * wink* * wink* "