I'm playing around a bit with writing a reactive programming lib for nim. Part of that means implementing Observers, which contain 3 callbacks: next, error and complete.
All 3 of these callbacks _must_ be async procs because they **might** contain async work done by the user. To keep that ability open I need to store them that way thusly and the type looks like this: type Observer*[T] = ref object next*: proc(value: T): Future[void] {.async.} error*: proc(error: ref CatchableError): Future[void] {.async.} complete*: proc(): Future[void] {.async.} Run Now to keep the syntax convenient though, I want people to be able to just pass me synchronous callback functions and I convert them to asynchronous ones under the hood (well, really only for the type system because without await they'll be executing synchronously anyway and just return an immediately completed Future). So the code I want to allow looks like this: newObserver[int]((value: int) => echo "Value is: ", value) newObserver[int]( proc(value: int) {.async.} = await asyncSleep(1000) echo "Value is: ", value ) Run My first draft of an implementation looks sth like this: proc newObserver*[T]( next: proc(value: T) | proc(value: T) {.async.}, error: proc(error: CatchableError) | proc(error: CatchableError) {.async.} = nil, complete: proc() | proc {.async.} = nil, ): Observer[T] = let nextProc = when next.isAsyncProc(): next else: proc(value: T){.async.} = next(value) ... repeat this for error and complete to assign to errorProc and completeProc... Observer[T]( next: nextProc, error: errorProc, complete: completeProc, ) Run Now the question is how to implement `isAsyncProc`. I know I could write a macro, but I feel like this should be solvable via simpler means. These are the things I've already tried: import std/[asyncdispatch, macros] proc a() {.async.} = echo "Potato" echo a is proc(): Future[void] {.async.} echo a is proc(): Future[void] echo a is proc() {.async.} echo a.hasCustomPragma(async) Run All of the above return false.