The author seems to be conflating IO and side effects. IO is just one of the many ways that side effects can occur.
> Purity also means you can't read or write global variables, that's just another type of IO.
Er... what? No, not at all.
Reading global variables is not side effects, much less IO.
Writing global variables is side effects, but has nothing to do with IO.
> Functions that do IO are difficult or impossible to test.
Absolutely not, IO functions are not harder to test than regular ones. Is your function creating a file? Start your test, assert the file doesn't exist, call your function, assert your file exists. Done.
> I now put all my IO in top-level functions that are called directly from main
This is great for homework, but in the real life, IO needs to be performed throughout your entire application, and unsafePerformIO is just not an option. You have to use iteratees for this kind of operation, which adds a pretty thick layer of complexity to your code base.
> boo :: Map Integer String -> String -> Integer
This syntax is clean but it doesn't tell me anything that
def boo(map: Map[Integer, String], key: String) : Integer
doesn't tell me.
> The point is, you can't confidently reason about a function from its signature if IO is involved.
Sure, you can: the mention of IO tells me this function operates within the IO monad, so I know a lot of operations and laws that apply to it.
Haskell is a fantastic language but this article is doing a very poor job at explaining why. I suspect its author has only recently started using Haskell and would have been better advised to wait a bit before sharing his enthusiasm.