当您提供相同代码的异步变体时,约定是什么?

What's the convention for when you offer an async variant of the same code?

foo为子或方法。我编写了一个阻塞变体和一个异步变体,所以从外部看,本质区别在于 return 值。我首先想到的是在签名中指定它,但不幸的是调度程序只查看传入端而不是两者:

> 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

我应该在签名中添加一个Bool :$async吗?我应该像 JS 一样添加名称后缀(即 foofoo-async)吗?两者对我来说都没有太大的意义。这个问题目前有哪些解决方案?

return 类型的多重分派无法工作,因为 return 值本身可以用作多重分派调用的参数(并且因为几乎 Perl 6 中的每个运算符都是多重分派打电话,这将是很常见的事情)。

至于手头的问题:考虑到核心代码、模块和我自己的一堆代码,给定的 class 或模块似乎通常会提供同步接口 一个异步接口,以最适合手头问题的方式为准。在两者都有意义的情况下,它们通常在类型或模块级别进行区分。例如:

  • 核心:有ProcProc::Async,还有IO::Socket::INETIO::Socket::Async。虽然有时可以通过为每个同步例程提供 Promise-returning 替代方案来获得合理的异步 API,但在其他情况下,整体工作流程会有点不同。例如,对于同步套接字 API 来说,坐在循环中请求数据是很合理的,而异步 API 在 Perl 6 中通过提供 Supply通过网络到达的数据包。
  • 库:Cro::HTTP::Client 提供一致的异步接口来执行 HTTP 请求。没有同步API.
  • 应用程序:考虑到我的很多应用程序代码,就 API 而言,事情似乎要么始终同步,要么始终异步。我发现的唯一例外是几乎完全同步的 classes,除了它们有一些 Supply-returning 方法以提供事件通知。然而,这并不是真正被问及的情况,因为通知自然是异步的。

有趣的是,我们在这里结束了,这与通过命名约定提供异步变体的各种其他语言形成鲜明对比。我认为大部分原因是可以在 Perl 6 中的任何地方使用 await。在具有 async/await 对的语言中情况并非如此,为了使用 await 必须首先将调用例程重构为 async,然后将其调用者重构为 async,等等

因此,如果我们正在编写一段完全同步的代码,并希望使用 return 是 Promise 的模块中的某些内容,我们的全部成本是 "just write await"。而已。在对 await 的调用中写入与 -sync-async 后缀或 :sync:async 命名参数的长度相同。

另一方面,人们可能会选择为某些东西提供同步 API,即使它在内部正在做 await,因为感觉大多数消费者只想同步使用它。如果有人希望异步调用它,还有另一个 5 个字母的单词 start,它将在线程池上触发它,而在代码内部执行的任何 await 都不会(假设 Perl 6.d) 阻塞了一个真正的线程,而是安排它在 awaited 工作完成后继续。同样,这与编写 async 后缀、命名参数等的长度相同或更短

这意味着我们似乎要结束的模式(考虑到对年轻语言的通常警告,以及需要时间发展的惯例)是:

  • 对于简单的情况:选择最常见的用例并提供,让调用者使用 start(同步 -> 异步)或 await/react( async -> sync) 如果他们想要其他东西
  • 对于更复杂的情况,使用该功能的同步和异步工作流可能看起来完全不同,但都很有价值:分别提供它们。当然,一个可能是另一个的外观(例如,核心中的 Proc 实际上只是 Proc::Async 之上的同步适配层)。

我最后要说的是,模块的个人消费者几乎肯定会同步或异步使用它,而不是两者的混合。如果希望同时提供两者,那么我可能会改用 export tags,所以我可以这样做:

use Some::Thing :async;
say await something();

或者:

use Some::Thing :sync;
say something();

并且不必在每次调用时声明我想要的。