Let foo
be sub or method. I have programmed a blocking and an async variant, so looking from the outside the essential difference is in the return value. I first thought of specifying it in the signature, but the dispatcher unfortunately only looks at the incoming end instead of both:
> multi sub foo (--> Promise) {}; multi sub foo (--> Cool) {};
> my Promise $p = foo
Ambiguous call to 'foo(...)'; these signatures all match:
:( --> Promise)
:( --> Cool)
in block <unit> at <unknown file> line 1
Should I add a Bool :$async
to the signature? Should I add a name suffix (i.e. foo
and foo-async
) like in JS? Both don't feel much perlish to me. What are the solutions currently in use for this problem?
Multiple dispatch on return type cannot work, since the return value itself could be used as the argument to a multiple dispatch call (and since nearly every operator in Perl 6 is a multiple dispatch call, this would be a very common occurrence).
As to the question at hand: considering code in core, modules, and a bunch of my own code, it seems that a given class or module will typically offer a synchronous interface or an asynchronous interface, whichever feels most natural for the problem at hand. In cases where both make sense, they are often differentiated at type or module level. For example:
Proc
and Proc::Async
, and IO::Socket::INET
and IO::Socket::Async
. While it's sometimes the case that a reasonable asynchronous API can be obtained by providing Promise
-returning alternatives for each synchronous routine, in other cases the overall workflow will be a bit different. For example, for a synchronous socket API it's quite reasonable to sit in a loop asking for data, whereas the asynchronous API is much more naturally expressed in Perl 6 by providing a Supply
of the packets arriving over the network.Cro::HTTP::Client
offers a consistently asynchronous interface to doing HTTP requests. There is no synchronous API.Supply
-returning methods in order to provide notifications of events. This isn't really the case being asked about, however, since notifications are naturally asynchronous.It's interesting that we've ended up here, in contrast to in various other languages where providing async variants through a naming convention is common. I think much of the reason is that one can use await
anywhere in Perl 6. This is not the case in languages that have an async
/await
pair, where in order to use await
one must first refactor the calling routine to be async
, and then refactor its callers to be async
, etc.
So if we are writing a completely synchronous bit of code and want to use something from a module that returns a Promise
, our entire cost is "just write await
". That's it. Writing in a call to await
is the same length as a -sync
or -async
suffix, or a :sync
or :async
named argument.
On the other hand, one might choose to provide a synchronous API to something, even if on the inside it is doing an await
, because the feeling is most consumers will just want to use it synchronously. Should someone wish to call it asynchronously, there's another 5-letter word, start
, that will trigger it on the threadpool, and any await
that is performed inside of the code will not (assuming Perl 6.d) block up a real thread, but instead just schedule it to continue when the await
ed work is done. That's, again, the same length - or shorter - than writing an async
suffix, named argument, etc.
Which means the pattern we seem to be ending up with (given the usual caveats about young languages, and conventions taking time to evolve) is:
start
(sync -> async) or await
/react
(async -> sync) if they want the other thingProc
in core is actually just a synchronous adaptation layer over Proc::Async
).A final observation I'd make is that individual consumers of a module will almost certainly be using it synchronously or asynchronously, not a mixture of the two. If wishing to provide both, then I'd probably instead look to using export tags, so I can do:
use Some::Thing :async;
say await something();
Or:
use Some::Thing :sync;
say something();
And not have to declare which I want upon each call.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With