"if you're doing OOP your code will have methods and "setters" that mutate or set internal state"
I don't think there is nothing in OOP that "forces" you to explicitly mutate state. People just happen to do it that way, even if there was no need for that.
I.e. instead of mutating an instance of class Foo
void Foo.set(Some bar)
There are several patterns that make instance of Foo immutable. As an example:
1. Use just 'Factory' pattern to build state and disregard mutation altogether FooFactory fact; fact.AddBar(Bar bar); Foo foo = fact.build();
This may appear as mutating (the factory) but the point is the mutations are located at the factory, which is expceted to have a limited scope, and the entity with larger scope (Foo) is immutable wihtout setters.
2. If there is need to modify existing state, instead of mutating the instance, return a new instance with the value modified. So instead of
void foo.Set(Bar bar)
Use method that creates a copy of foo, modifies state, and then returns a new instance of Foo
Foo foo.Modify(Bar bar)
Just removing mutability actually goes long way in making programs more legible akin to functional programs.
I agree functional style is often the best. Unfortunately we just don't have a functional language that could replace C++.