Apologies if this is the wrong thread!
on the caller side you’d have async functions only. So function color becomes a problem.
I may be misreading you, or may have been unclear before, but the (offhand) pitch i was making was for having the behavior differ on the caller side so that a call without awaiting would be synchronous, and a call with await would be async.
so this:
if x = y(); await x and await y() have different meanings.
would not be a problem (unless the author chose to make it one) - in x = y(), x would not be awaitable, since it would run __call__. the purpose of the pitch was to make it so y() and await y() don’t have different meanings, but one can write an async-optimized version of the synchronous y without needing to have two methods with different names.
The motivation for me was a case very similar to the example in your comment:
from abc import ABC, abstractmethod
class Runner(ABC):
@abstractmethod
def process(self): ...
class SyncRunner(Runner):
def process(self):
# do something synchronously...
return "x"
class AsyncRunner(Runner):
async def process(self):
# do something asynchronously...
return "x"
where I want to have an ABC/protocol-like class with a common calling convention, but the async/sync implementations are substantially different enough that they make sense as being separate classes. I can mask all of that with existing languages except for the coloring problem, where a caller then always has to defensively write
if iscoroutine(runner.process):
await runner.process()
else:
runner.process()
if they want runner to be arbitrary across any subclass of Runner (there also isn’t a way to type hint this as far as i’m aware).
I could write
class Runner:
def process(): ...
async def aprocess(): ...
but then i have the problem of having that propagate through the rest of the class, where i also need to have async/sync versions of all the other private methods for want of a way to define doing “the same thing” async vs sync.
one could also imagine it being a decorator like this a la property setters
class Runner:
def process(): ...
@process.async
async def process(): ...
that does something like this under the hood
#async decorator on the `process` method
class MethodType:
def async(self, func):
self.__acall__ = func
I don’t think it would be syntactically surprising except in the case that one wants to explicitly create the coroutine object without awaiting, but then that would just be, somewhat inelegantly:
coro = runner.process.__acall__()
await coro
but that would still be backwards compatible because there currently isn’t a mechanism to have dual-color methods, so existing async methods would have to opt-in to the new behavior, giving time for warnings and whatnot.
this can’t be done with code generation, I don’t think, which is why I thought it was worth thinking about as a language mechanism. The only way i found to actually implement this is to get the call stack and inspect the calling frame to see if it was awaiting the method, but that is janky and error prone.