【问题标题】:How to implement function/process polymorphism in Elixir如何在 Elixir 中实现函数/进程多态性
【发布时间】:2015-12-10 11:53:22
【问题描述】:

我想以几种(独立)方式“扩展”Elixir 的内置 IO/文件功能。

我想出了以下代码模式:

defmodule FileExtension1 do
  # arguments are different for different extensions,
  # but I guess I could just stick with single map/list
  # argument for uniformity 
  def open!(filename, some_param \\ true) do
    filename
      |> File.open!
      |> attach(some_param)
  end

  def close(io) do
    io |> File.close
  end

  def attach(io, some_param \\ false) do
    spawn_link fn ->
      file_manager(io, some_param)
    end
  end

  def detach(io) do
    io |> send {:detach, self}
    receive do
      {:will_detach, ^io} -> :ok
    end
  end

  defp file_manager(io, some_param, state \\ <<>>) do
    if Process.alive?(io) do
      receive do
        {:detach, sender} ->
          sender |> send {:will_detach, self}
        {:custom_request, sender, reference, count} ->
          # {result, new_state} = do_job(...)
          sender |> send {:custom_reply, reference, result} 
          file_manager(io, some_param, new_state)
        {:io_request, sender, reference, {:some_pattern}} ->
          # {result, new_state} = do_job(...)
          sender |> send {:io_reply, reference, result}
          file_manager(io, some_param, new_state)
        x ->
          io |> proxy(x)
          file_manager(io, some_param, state)
      end
    end
  end

  defp proxy(io, data) do
    {request_type, original_from, original_reference, command} = data
    reference = make_ref
    io |> send {request_type, self, reference, command}
    receive do
      {response_type, ^reference, result} -> original_from |> send {response_type, original_reference, result}
    end
  end
end

基本上,它执行以下操作:

  • 根据自定义协议处理自定义 IO 请求
  • 修改了一些标准 IO 请求的处理
  • 将其他所有内容代理到底层File

现在我可以透明地将这些东西堆叠在一起(即attach 第一个到File,然后第二个到第一个等等)。

问题是:现在我有三个模块,它们遵循我上面描述的相同模式。我想以某种方式删除代码重复。

我应该调查什么?

  • 只是创建另一个具有共享功能的模块?就像IO 包含多个设备的共享功能,或者Enum 包含多个类型的共享功能。你能给我一个小例子吗?
  • 协议?我不太明白如何在这里使用协议,因为我想要实现的并不属于“为不同(内置)类型使用某些函数”模式。
  • 行为?看起来我可以从创建GenServer 的一些变体中受益,根据我的需要进行调整。再说一次,如果我应该使用它,这里的小例子会有所帮助。

额外问题:如何使用 ExUnit 测试共享功能?

【问题讨论】:

  • 为什么要创建不同的函数?如果您需要针对不同文件扩展名的不同行为,只需将行为传入即可。说真的——只需传入处理文件扩展名的函数即可。你让它变得比原来要困难得多。
  • @OnorioCatenacci 我喜欢这个主意。您能否添加一个非常简约的示例作为答案?
  • 如果我有时间,是的,我会添加一个例子。

标签: polymorphism elixir


【解决方案1】:

将一个函数传递给另一个函数的一个非常简单的例子:

#PassInFunction
defmodule PIF do
  def hello(name), do: IO.puts("Hello #{name}!")
  def execf(name, f), do: f.(name)
end

PIF.execf("Onorio",&PIF.hello/1)

在您的特定情况下,我会使用文件类型(或扩展名,视情况而定)来确定要传入的函数。

【讨论】:

    猜你喜欢
    • 2011-06-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-04
    相关资源
    最近更新 更多