您如何扩展/继承e剂模块? [英] How do you extend/inherit an elixir module?
问题描述
让我们说一个灵丹妙药库定义:
Let's say an elixir library defines:
defmodule Decoder do
def decode(%{"BOOL" => true}), do: true
def decode(%{"BOOL" => false}), do: false
def decode(%{"BOOL" => "true"}), do: true
def decode(%{"BOOL" => "false"}), do: false
def decode(%{"B" => value}), do: value
def decode(%{"S" => value}), do: value
def decode(%{"M" => value}), do: value |> decode
def decode(item = %{}) do
item |> Enum.reduce(%{}, fn({k, v}, map) ->
Map.put(map, k, decode(v))
end)
end
end
我想定义一个模块 MyDecoder
仅向上述模块添加一个 def解码
。用oo语言,这可以通过某种继承/混合/扩展来实现。
I want to define a module MyDecoder
which just adds one more def decode
to the above module. In an oo language, this would be done by inheritance/mixin/extends of some sort.
我如何在长生不老药中做到这一点?
How do I do this in elixir?
推荐答案
显然可以。看看此要点,它使用一些相当晦涩的方法来列出模块的公共功能然后从中产生代表。
Apparently, you can. Take a look at this gist which uses some rather "obscure" methods for listing a module's public functions and then generating delegates out of them. It's pretty cool.
这就是全部内容:
defmodule Extension do
defmacro extends(module) do
module = Macro.expand(module, __CALLER__)
functions = module.__info__(:functions)
signatures = Enum.map functions, fn { name, arity } ->
args = if arity == 0 do
[]
else
Enum.map 1 .. arity, fn(i) ->
{ binary_to_atom(<< ?x, ?A + i - 1 >>), [], nil }
end
end
{ name, [], args }
end
quote do
defdelegate unquote(signatures), to: unquote(module)
defoverridable unquote(functions)
end
end
end
您可以这样使用它:
defmodule MyModule do
require Extension
Extension.extends ParentModule
# ...
end
不幸的是,它对最新的Elixir版本发出警告,但我敢肯定被解决。
Unfortunately, it throws a warning on the most recent Elixir builds, but I'm sure that can be solved. Other than that, it works like a charm!
编辑以免发出警告:
defmodule Extension do
defmacro extends(module) do
module = Macro.expand(module, __CALLER__)
functions = module.__info__(:functions)
signatures = Enum.map functions, fn { name, arity } ->
args = if arity == 0 do
[]
else
Enum.map 1 .. arity, fn(i) ->
{ String.to_atom(<< ?x, ?A + i - 1 >>), [], nil }
end
end
{ name, [], args }
end
zipped = List.zip([signatures, functions])
for sig_func <- zipped do
quote do
defdelegate unquote(elem(sig_func, 0)), to: unquote(module)
defoverridable unquote([elem(sig_func, 1)])
end
end
end
end
这篇关于您如何扩展/继承e剂模块?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!