| The problem with function color exists when you can't abstract over it[1] Some statically typed languages (I believe both Haskell, ocaml) have powerful type system that allow abstracting over function types and function colors. Color is not an issue here. Some other statically typed languages (C#, rust, and C++ (at least with the built-in stackless coroutines)) can abstract over types but not over colors. This is a problem. Some statically typed languages (Go) do not encode async-ness statically, so it is not an issue[2]. Some dynamically typed languages (scheme, Lua, lisp) also do not encode async-ness statically. Everything is fine. Finally there are some dynamically typed languages (python, js) that, eskew static types but for some reason still decide to encode async-ness statically. For me this is the most bizarre decision, especially as some of the justifications for static async-ness (performance, memory usage) are less relevant. [1] for example, a litmus test is being able to implement an higher order function that inherits its color from one of its parameters. Essentially this is the problem of generically turning an internal iterator to an external one. [2] fundamentally in these languages continuations are first class values that can be passed around, so the asyncness is naturally not bound to the function that created a continuation. Edit: you can in principle abstract away color in any async language by simply assuming that any function call is async and await it. Then sync functions can trivially be made async. But at this point async annotations no longer convey any useful property: the language might as well implicitly await any function and require call-site annotations for diverging control flow or reentrancy requirements. More practically as languages with async evolve and grow asyncness becomes pervasive and pushes away any sync component. |
Hopefully it's safe read this as there's no common static type between function and async function meaning APIs (that take functions as arguments) have to provide seperate methods (or overloading) for these different colours.
Like in typescript you can write `<T>(f: () => T) => T` because an async function statically is just the return type wrapped in a Promise, not something like `async () => T` you can still pass in an async function as an argument.
I think that's a reasonable thing to take issue with, and its _possibly_ an avoidable design problem. That said I can see it being less avoidable if the async function requires some special kind of invocation (like being associated with some kind of async runtime and its a compiled language).
When I see people bring the issue of function colouring, the focus tends to be on the fact that a function is no longer interchangeable with a sync function and now you have to handle a promise, which I personally find unconvincing if the return type really should be a promise then it shouldn't be interchangeable with a sync function.