The way dependency injection is implemented in mainstream languages usually involves using metaprogramming to work around the language, not with the language. It's not uncommon to get errors in dependency-injected code that would be impossible to get with normal code.
It's interesting to see how things can work if the language itself was designed to support dependency injection from the get-go. Algebraic effects is one of the ways to achieve that.
But they also introduce their own color-like problems.
For example with Scala we have ZIO which is an effect system where you wrap all your code in their type e.g. getName(): ZIO[String]. And it doesn't matter if getName returns immediately or in the future which is nice.
But then the problem is that you can't use normal operators e.g. for/while/if-else you need to use their versions e.g. ZIO.if / ZIO.repeat.
So you don't have the colour problem because everything is their colour.
Functions can be polymorphic in their effectfulness, so the coloring problem isn't. Functions only become incompatible where you've made them incompatible on purpose - the whole point of annotating functions' effectfulness is to statically know you're not accidentally invoking particular effects where you promised you wouldn't.
Maybe but even if that's the case, you've now got just plain and colored vs before you'd have plain, color A, color B, color C if you go beyond just async as the only other color (e.g. whether you're accepting something by reference or value, whether it's a mutable reference, whether or not the function is compile-time, etc etc etc).
Still seems better to me if you collapse colors >= 1 into a single language system.
Developer of Ante here. Functions in languages with effect systems are usually effect polymorphic. You can see the example in the article of a polymorphic map function which accepts functions performing any effect(s) including no effect(s). For this reason effect systems are one of the solutions to the "what color is your function" problem.
It's interesting to see how things can work if the language itself was designed to support dependency injection from the get-go. Algebraic effects is one of the ways to achieve that.