Ask not how to get rid of the evil mutable state, but rather how to tame it by provding structured and predictable ways of using it.
goto is harmful you wouldn't remove all flow control from a language, right? You'd rather think of ways of using control flow in a structured way, and then you would invent the
There is not single best solution on how to structure and control mutable state, but there are some good ones. For example, you can have the programmer describe quite explicitly which part of the program can do what and who "owns" stateful parts of memory. The compiler will then check for compliance. An example of this sort of thing is Rust.
There is a more general phenomenon which encompasses mutable state, namely computational effects. Mutable state is tricky to get right, but so is concurrency, I/O can be tricky depending on what you do, etc. These are all computational effects, and we can ask whether there are general and structured ways of dealing with them.
The most popular general way of controlling computational effects is to use monads. These are programming language constructs that allow you to express presence of certain computational effects (could be mutable state) with typing information. This way the programmer will be able to see that a certain piece of code may cause effects, and the compiler will complain if things don't fit.
Another method that has caught some interest lately is algebraic effects and handlers. It's less general than monads, but it also organizes your code in a way that is often a bit more flexible than monads (The people of the Internet want to know this too.) Briefly, handlers are a generalization of the familiar exception handlers that allow you to intercept any computational effect and do something with it. I like to explain their usefulness with the slogan
That is, just like
while is the structured way to do
goto, handlers are the structured way to do non-local flow control in a functional setting (
callcc has been called "the
goto of functional programming").