Adding colors without a concrete reason to do so will always be a waste of time. Take type checking as an example. If you use type A as a parameter that requires type B, your program will not work. Type checking provides a lot of value. Or in the GP’s example, forcing functions into async contexts ensures that, for example, the main UI thread doesn’t get blocked. The program will appear broken to the user if this rule is broken. Now compare this to pure functions. What happens if you break the pure function contract? Probably nothing. It’s still totally possible for the program to work. So you end up doing a lot of work that ultimately doesn’t change anything, which is what you’re observing with the proliferation of impure code.
If a compiler assumes that a function is pure, while it's actually not pure, it may end up performing optimizations that break the logic, such as changing `x = f(); y = f()` into `x = f(); y = x`. And so might a human when refactoring.
This illustrates my point perfectly. Theoretical refactoring or optimizations are just hypothetical situations that may or may not occur. Needing to proliferate a function color incurs a real cost in terms of development efficiency. You should get something concrete if you pay the price. Until that happens, such language features are unlikely to gain widespread popularity.
I think the x87 status/config registers you’re alluding to are a great example of power to the compiler that function colouring can potentially grant. “De-colouring” the function requires gating around preserving these registers but within that colour/monad there’s no need to preserve them. That’s an upside, not a downside