2015-09-14 5 views
3

Я хочу «расширить» встроенные функции ввода/вывода Elixir несколькими (независимыми) способами.Как реализовать полиморфизм функций/процессов в Elixir

я придумал следующий шаблон кода:

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 запросов в соответствии с пользовательским протоколом
  • Модифицирует обработки некоторыхов стандартные запросы ввода-вывода
  • Прокси-серверы все остальное до базового File

Теперь я могу прозрачно складывать эти вещи друг на друга (т. attach первый на File, затем второй на первый и т. Д.).

Проблема: теперь у меня есть три модуля, которые следуют тому же образцу, который я описал выше. Я хочу как-то удалить дублирование кода.

На что я должен смотреть?

  • Просто создайте еще один модуль с общими функциями? Например, IO содержит общие функции для нескольких устройств, или Enum содержит общие функции для нескольких типов. Не могли бы вы дать мне небольшой пример того, как это будет работать в моем случае?
  • Протоколы? Я не совсем понимаю, как протоколы можно использовать здесь, потому что то, что я пытаюсь достичь, не распространяется на «использование некоторой функции для разных (встроенных) типов».
  • Поведение? Похоже, я мог бы выиграть от создания какого-то варианта GenServer, настроенного на мои нужды. Опять же, если я буду использовать это, небольшой пример поможет здесь.

Вопрос с бонусом: Как проверить, что общая функциональность используется с помощью ExUnit?

+0

Почему вы создаете различные функции? Если вам нужно другое поведение для разных расширений файлов, просто передайте поведение. Серьезно - просто передайте функцию для обработки расширения файла. Вы делаете это намного сложнее, чем должно быть. –

+0

@OnorioCatenacci Мне нравится эта идея. Не могли бы вы добавить действительно минималистический пример в качестве ответа? – EugZol

+0

Если у меня будет время, да, я добавлю пример. –

ответ

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) 

В вашем конкретном случае я хотел бы использовать тип файла (или расширение в зависимости от обстоятельств может быть), чтобы определить, какие функции передать в .