您如何扩展/继承e剂模块? [英] How do you extend/inherit an elixir module?

查看:57
本文介绍了您如何扩展/继承e剂模块?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

让我们说一个灵丹妙药库定义:

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆