You're right, but ST is also implemented using unsafePerformIO. The larger point is that you don't need to dress up a type signature with ST or IO just to use imperative code internally which can't leak out to the user level. Whether this is good or bad is a matter for debate, but used judiciously I think it's good.