如何创建"抽象" Elixir中的模块或"虚拟"功能?

时间:2016-06-22 20:16:24

标签: elixir

我知道我有点像Ruby-ish甚至是Java-ish,但我经常会遇到这种情况。

我想定义一些通用模块(可以使用元编程),它将使用一些将在" children"中指定的变量/函数。模块(可能use这个通用模块。)

我想它就像:

defmodule Parent do
  defmacro __using__(_) do
    quote do
      def function do
        __MODULE__.other_function
      end
    end
  end
end

以及后来:

defmodule Child do
  use Parent

  def other_function do
    # some real work
  end
end

我经常需要"抽象"一些函数,最好的是通过定义一些模块变量@var可以访问" parent"模块,但我知道它并没有像这样工作。

有没有办法调用将在&#34中定义的函数;包括"模块?

2 个答案:

答案 0 :(得分:3)

我认为你正走在正确的轨道上。如果你添加@callback来为"抽象"定义spec功能会更好。我将用一个工作实例来解释:

defmodule Fibonacci do
  @callback fib(number :: integer) :: integer

  defmacro __using__(_) do
      quote do
        @behaviour Fibonacci

        def fibonacci(n) when not is_integer(n), do: {:error, "Not integer"}
        def fibonacci(n) when n < 0, do: {:error, "Negative number"}
        def fibonacci(n), do: {:ok, __MODULE__.fib(n)}

        def fibonacci!(n) do
          case fibonacci(n) do
            {:error, reason} -> raise reason
            {:ok, n} -> n
          end
        end
      end
  end
end

使用Fibonacci的每个模块都需要实现回调函数fib/1。此外,use Fibonacci模块的每个模块都将具有处理错误的函数fibonacci/1和引发错误的fibonacci!/1

所以,让我们直接递归实现fib/1

defmodule Direct do
  use Fibonacci

  def fib(0), do: 0
  def fib(1), do: 1
  def fib(n), do: fib(n - 1) + fib(n - 2)
end

对于尾递归:

defmodule Tail do
  use Fibonacci

  def fib(0), do: 0
  def fib(1), do: 1
  def fib(n), do: fib(1, 1, n - 2)

  defp fib(_, value, 0), do: value
  defp fib(n_2, n_1, n), do: fib(n_1, n_2 + n_1, n - 1)
end

所以在iex我们可以调用不同的实现:

iex(1)> Direct.fibonacci(10)
{:ok, 55}
iex(2)> Tail.fibonacci!(10)
55
iex(3)> Tail.fibonacci!(-10)
** (RuntimeError) Negative number

另外,如果您忘记在模块中定义fib/1函数,那么编译器会向您发出警告:

defmodule Warning do
  use Fibonacci
end

* warning: undefined behaviour function fib/1 (for behaviour Fibonacci)

如果您想使用此代码,请点击此链接 http://elixirplayground.com?gist=606fe8c283443f03c7af08f85c888fe3

我希望这能回答你的问题。

答案 1 :(得分:2)

根据您的确切用例,可能有多种方法可以执行此操作。最简单的方法就是使用更高阶的函数而不使用任何元编程。

defmodule Parent do
  def function(function_passed_as_variable) do
    function_passed_as_variable.()
  end
end

defmodule Child do
  def function do
    Parent.function(&other_function/1)
  end
  def other_function do
    #some real function
  end
end

我保留了名称ParentChild来引用您的代码,但在这种情况下它不适合。

要实现多态性(一个函数根据数据执行不同的操作),您可以使用模式匹配:

def function({:first_type, data}), do: data*2
def function({:second_type, data}), do: data+2
def function(data), do: data

还可以为不同的结构和其他数据类型定义协议。

选择正确的模式可能并不容易,所以请随意评论更具体的例子。