【问题标题】:Why Elixir doesn't use Multimethods / Protocols?为什么 Elixir 不使用多方法/协议?
【发布时间】:2016-02-08 16:27:42
【问题描述】:

让我们看看文档中的示例:

square = fn(x) -> x * x end
list = [1, 2, 3, 4]
Enum.map(list, square)

为什么需要显式写Enum.map?为什么它不使用简洁的符号map [1, 2, 3, 4], square

Elixir 有多个调度和协议,但在我看来,它的使用有点奇怪。

如果您考虑 OOP 中的多态性或 FP 中的多方法/多分派、协议,重点是使代码简短明了,并让程序员不必记住方法的来源。

因此,在 OOP 中,代码如下所示:

 list.map(square) 

在 FP 多方法中它看起来像

map(list, square)

在这两种情况下,编译器/解释器都使用参数类型来确定它应该使用什么map 方法。

为什么 Elixir 不使用相同的方法?为什么它需要编写冗长的代码并将决定函数来自何处的责任放在程序员的肩上?

有时不使用多方法并明确指定它是有意义的,例如HttpServer.start(80)。但是对于像eachgetsetsize 等一般方法,在没有明确指定它的来源的情况下使用它似乎要容易得多。

附:

Elixir 中的 Protocols 似乎实际上可以做到这一点。我想知道-为什么不使用它?我在 GitHub 上看到的 Elixir 项目中的所有代码都使用冗长的符号,例如 ModuleName.fnName。不知道为什么会这样。是否不鼓励使用协议,或者过于复杂而无法在日常任务中使用?

【问题讨论】:

  • 我相信没有人愿意为了生存而编写协议实现。如果您确切知道您的数据是列表 - 您编写列表。
  • 这是一个相当广泛的问题。投票结束。
  • 请注意,这不是 OOP。命名空间的工作方式、合法传递的内容、存在的数据原语、消息和函数/方法调用之间的明显区别以及even the basic unit of computation 都完全不同。

标签: functional-programming protocols elixir multimethod


【解决方案1】:

您可以以可扩展的方式将Enum.map 与不同的参数一起使用,因为它是通过协议实现的:

iex> Enum.map [1, 2, 3], fn x -> x * x end
[1, 4, 9]

iex> Enum.map 1..3, fn x -> x * x end
[1, 4, 9]

你也可以将Enum.map写成map,只要导入Enum模块即可:

iex> import Enum
iex> map [1, 2, 3], fn x -> x * x end
[1, 4, 9]

默认情况下,我们根本不包含 Enum 模块。显式导入它会更好,这样任何阅读您的代码的人都会更好地了解所使用的函数来自何处。

换句话说,多个调度和协议仍然没有改变代码始终存在于模块中并且调用始终是合格的这一事实,除非导入。

【讨论】:

  • 感谢回复,但协议不是多方法/多态。你不能同时从不同的模块导入map,比如说Enum.mapMap.map——所以很明显没有多态性。
  • Elixir 中的协议提供了开放的 ad-hoc 多态性。上面的响应显示了它在第一个参数上是如何多态的。甚至 Elixir 中的常规函数​​子句也提供了一种称为封闭即席多态的多态形式。多方法是另一种形式的多态,Elixir 不支持。泛型也是多态的另一种形式,称为参数多态,甚至继承也称为子类型多态。多态性是一个非常广泛的术语,因此请注意不要混淆。
【解决方案2】:

在 Elixir(和 Erlang)中,函数总是存在于模块中。没有模块就没有功能。甚至你称之为“裸”的函数也在一个模块中——它被称为Kernel(在 Erlang 中,它们位于 :erlang 模块中)。

Erlang/Elixir 中的函数由模块、名称和数量标识 - 只有这三个元素的组合才能告诉您函数的真实身份,但所有这三个元素都只关心函数的命名 - 而不是数据它起作用了。

相反,协议不是关于命名函数的,而是关于处理多态数据的。所有Enum 函数都由Enumerable 协议支持——它们可以处理列表、范围、映射、集合等。所以Enum.map 是一个多态函数,但它被称为Enum.map 而不仅仅是map。如果你对模块名称很困扰,你可以随时导入你想“裸”使用的模块。

【讨论】:

    【解决方案3】:

    因此,在 OOP 中,代码如下所示:

    list.map(正方形)

    在 FP 多方法中它看起来像

    地图(列表,正方形)

    在 OOP 中,您获取一个对象(您的列表)并调用该对象正在实现的方法,在本例中为 map

    在 Elixir 中,您可以调用存储在 Enum 模块中的 map 函数,其中包含您的集合和要应用的函数。 在文档等方面,平面命名空间并不是那么好。通过引用 Enum 下所有与可枚举相关的函数,您的文档更有意义(但这不是唯一的原因)。

    【讨论】:

      【解决方案4】:

      非常刚接触 Elixir。

      结构和协议仅在第一个参数上解决多态性对我来说似乎是有限的。我发现像 Julia、CLOS 和 Clojure 这样的多方法/多分派更强大。

      但是,使用带有 defguard 宏的多参数保护,您似乎可以毫不费力地非常接近多方法。

      一个简单得可笑的例子看起来像......

      defmodule MyModule do
        defguard both_integers(i, j) when is_integer(i) and is_integer(j)
      
        def add(i, j) when both_integers(i, j) do
          IO.puts i + j
        end
      
        def add(i, j) do
          IO.puts "do something completely different"
        end
      end
      

      【讨论】:

        猜你喜欢
        • 2016-02-15
        • 2013-07-09
        • 2023-03-11
        • 2018-06-22
        • 1970-01-01
        • 2016-09-02
        • 2011-12-25
        • 2014-07-25
        • 1970-01-01
        相关资源
        最近更新 更多