为什么Elixir不使用Multimethods / Protocols?

时间:2015-11-07 16:45:29

标签: functional-programming protocols elixir multimethod

让我们看一下docs的例子:

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中的多态性或MultiModods / Multiple Dispatch,FP中的协议,重点是让代码简短,简洁,让程序员记忆从记忆方法的来源中解放出来。

所以,在OOP中,它看起来像下面的代码:

 list.map(square) 

在FP multi方法中,它看起来像

map(list, square)

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

为什么Elixir不使用相同的方法?为什么需要编写详细的代码并负责决定函数的来源于程序员的肩膀?

有时候不使用多方法并明确指定它是有意义的,比如HttpServer.start(80)。但对于eachgetsetsize等常规方法,似乎在没有明确指定来源的情况下使用它会更容易。

P.S。

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

4 个答案:

答案 0 :(得分:11)

您可以以可扩展的方式使用具有不同参数的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模块。明确导入它会好得多,所以任何阅读代码的人都可以更好地了解使用函数的来源。

换句话说,多个调度和协议仍然不会改变代码始终存在于模块内部的事实,并且除非导入,否则调用始终是合格的。

答案 1 :(得分:2)

在Elixir(和Erlang)中,函数始终存在于模块中。你没有模块就没有功能。甚至你正在呼唤的功能"裸露"在一个模块中 - 它被称为Kernel(在Erlang中它们位于:erlang模块中)。

Erlang / Elixir中的函数由模块,名称和arity标识 - 只有这三个元素的组合可以告诉您函数的真实身份,但所有这三个元素仅涉及函数的命名 - 而不是数据它起作用。

相反,协议与命名函数无关 - 它们与处理多态数据有关。所有Enum函数都由Enumerable协议支持 - 它们可以使用列表,范围,映射,集等。因此Enum.map是一个多态函数,但它被称为Enum.map而不只是map。如果你对模块名称感到困扰,你可以随时导入你想要使用的模块"裸"。

答案 2 :(得分:0)

  

所以,在OOP中,它看起来像下面的代码:

     

list.map(square)

     

在FP multi方法中,它看起来像

     

地图(列表,方块)

在OOP中,您获取一个对象(您的列表)并调用此对象正在实现的方法,在这种情况下为map

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

答案 3 :(得分:0)

非常是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