为什么 Elixir 的访问行为不是协议?

Why is Elixir's Access behaviour not a protocol?

在最新版本的 Elixir 中,Access 不再作为协议实现,而是作为行为实现。据我所知,此更改是出于开发模式中的性能考虑。

现在,看看实现,我想知道它在内部是如何工作的以及为什么选择这个实现。正如我们所见 here,Access 通过底层映射的“__struct__”键分派到结构的模块。 AFAICS,这大致类似于 OOP 样式的多态性。关于这个的几个问题:

  1. 为什么这样更快?
  2. 与协议相比有哪些缺点?据我所知,它的可扩展性较差。还有其他的吗?
  3. 到目前为止,我只看到像 GenServer 这样的上下文中的行为,回调模块在初始化时被捕获并保存在一个进程中(至少我假设如此)。在这里,Access 行为从数据中获取回调模块。甚至可以为非结构的东西实现这种行为吗?
  4. 当人们对协议带来的额外好处不感兴趣时​​,这种调度是否是 Erlang 或 Elixir 中常见的最佳实践?

正如您已经提到的,Access 的实现已更改为使用行为而不是协议。推理是性能。

  • 协议 是基于 type/data 的多态性,并且是 Elixir 独有的。这意味着它可以让你根据数据结构进行调度
  • 行为 是一种无类型的机制,它不依赖于数据结构,而是将模块作为参数。它们也内置在 Erlang/OTP ecosystem 中并且性能更高。

虽然协议在基于数据类型进行调度时为您完成了很多繁重的工作,但由于它们的实施方式(合并),它们仍将 never be fast enough 用于 Access

因此,虽然当您需要将某些功能绑定到数据结构而不是模块时,您应该始终使用协议,但 Access 是一种特殊情况。

Because the Access protocol relies on the code server in development and test mode (when protocol consolidation is not applied), we have heard multiple reports of the system suffering greatly in performance as the code server becomes a bottleneck with multiple processes.

This happens because the Access protocol ends up being invoked thousands of times and there isn't much we can do to improve it (in contrast to the Enumerable protocol where most of the list cases are inlined).

~ Jose Valim


进一步阅读: