Elixir的多态性

多态性是向不同类型的实体提供单一接口。本质上,它允许不同的数据类型响应相同的函数。因此,相同的函数为不同的数据类型完成相同的行为。Elixir语言有‘协议’和简洁的方式实现多态性。

让我们实现一个基本的协议,它将开氏和华氏温度转换为摄氏温度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
defmodule Kelvin do
defstruct name: "Kelvin", symbol: "K", degree: 0
end
defmodule Fahrenheit do
defstruct name: "Fahrenheit", symbol: "°F", degree: 0
end
defmodule Celsius do
defstruct name: "Celsius", symbol: "°C", degree: 0
end
defprotocol Temperature do
@doc """
Convert Kelvin and Fahrenheit to Celsius degree
"""
def to_celsius(degree)
end
defimpl Temperature, for: Kelvin do
@doc """
Deduct 273.15
"""
def to_celsius(kelvin) do
celsius_degree = kelvin.degree - 273.15
%Celsius{degree: celsius_degree}
end
end
defimpl Temperature, for: Fahrenheit do
@doc """
Deduct 32, then multiply by 5, then divide by 9
"""
def to_celsius(fahrenheit) do
celsius_degree = (fahrenheit.degree - 32) * 5 / 9
%Celsius{degree: celsius_degree}
end
end

现在,我们实现了开氏和华氏温度的转换。

让我们尝试一些转换:

1
2
3
4
5
6
7
8
9
iex> fahrenheit = %Fahrenheit{degree: 45}
%Fahrenheit{degree: 45, name: "Fahrenheit", symbol: "°F"}
iex> celsius = Temperature.to_celsius(fahrenheit)
%Celsius{degree: 7.22, name: "Celsius", symbol: "°C"}
iex> kelvin = %Kelvin{degree: 300}
%Kelvin{degree: 300, name: "Kelvin", symbol: "K"}
iex> celsius = Temperature.to_celsius(kelvin)
%Celsius{degree: 26.85, name: "Celsius", symbol: "°C"}

让我们试着转换没有实现to_celsius函数的任何其他数据类型:

1
2
3
4
iex> Temperature.to_celsius(%{degree: 12})
** (Protocol.UndefinedError) protocol Temperature not implemented for %{degree: 12}
iex:11: Temperature.impl_for!/1
iex:15: Temperature.to_celsius/1

如果你想要转换所有数据类型,你可以为Any这个数据类型定义一个实现。最后,如果你有时间,请阅读 EnumString.Char 的源码,它们是Elixir核心代码里多态性的好例子。

原文链接: https://medium.com/@mustafaturan/polymorphism-in-elixir-cd0c765b6929#.d09i4bf56