At some point you need to break the paradigm to do anything useful, be it some IO, or whatever. Working in a FP way for as long as appropriate will make large parts of your program better testable and avoid large amounts of unwelcome surprises. If at some point you got some result, which you want to let affect the real world, then you can still limit mutation to that place, where you got the result. I would avoid that for as long as possible, to have larger parts of my program be easy to reason about.
GUI has been one of the strongholds of mutation-encouraging paradigms though. It should be said. I would use it as an example, where one probably might need mutation for performance reason and for the reason, that I find it a bit too overheady to create a new widget for every small change. I don't know enough about how some languages try to solve this and stay declarative or functional, to comment on that.