Проверьте, что модуль реализует поведение - PullRequest
2 голосов
/ 30 марта 2019

У меня есть поведение и функция, которая принимает список модулей, которые должны реализовать это поведение. Я хотел бы проверить, что каждый переданный модуль действительно реализует это поведение. Я могу сделать это с MyBehaviour.implemented_by?/1 ниже, но мне интересно, есть ли более прямой путь к этому.

defmodule MyBehaviour do
  @callback do_something(String.t(), String.t()) :: no_return()

  def implemented_by?(module) do
    :attributes
    |> module.module_info()
    |> Enum.member?({:behaviour, [__MODULE__]})
  end
end

Это лучший способ проверить это? Я не нахожу ничего на форуме по документам, Elixir или где-либо еще.

Должен ли я вообще это проверять? Или я должен просто позволить ответственности полностью возложить на абонента? Поведение в большей степени относится к «я хочу убедиться, что я реализую все необходимое», чем «я хочу, чтобы все остальные знали, что я реализую все необходимое»? *

Есть ли способ использовать поведение как тип в спецификациях типов? Может ли моя спецификация функции сказать, что аргументы должны реализовывать мое поведение, или я должен просто использовать module()/atom()?

1 Ответ

3 голосов
/ 31 марта 2019

Интересный вопрос.

Является ли поведение в большей степени «Я хочу убедиться, что я реализую все необходимое», чем «Я хочу, чтобы все знали, что я реализую все необходимое»?

Мое понимание таковочто поведение - это контракт между автором модуля и пользователем этого модуля, который говорит: «Я ожидаю, что вы предоставите мне модуль, который может делать все эти вещи».Поэтому ответственность за это лежит на пользователе модуля.

Тот факт, что ключевым словом для функции поведения является @callback, мне кажется, говорит о том, что обычно модуль, определяющий поведение, также является тем модулем, который будет использовать это поведение (другими словами, вызывая обратный вызов).).Похоже, что ответственность за реализацию поведения лежит на том, кто реализует поведение, с проверкой во время компиляции, чтобы помочь им, но нет никакой помощи во время выполнения для пользователя модуля, которому требуется поведение для выполненияЯ уверен, что они действительно предоставили правильную реализацию.

Ваше решение для предоставления предупреждения во время выполнения выглядит хорошо для меня - однако возможно реализовать поведение без предоставления атрибута @behaviour, поэтому оно не будет работать вэтот случай.

Существует немного более полезное сообщение об ошибке, если разработчик поведения объявил @behaviour в своем коде, но проигнорировал предупреждение компилятора:

предупреждение: требуется функция foo / 0по поведению ExpectBehaviour не реализовано (в модуле ClaimsItImplementsButDoesNot)

iex> ExpectBehaviour.use_behaviour(ClaimsItImplementsButDoesNot)
** (UndefinedFunctionError) function ClaimsItImplementsButDoesNot.foo/0 is undefined or private,
   but the behaviour ExpectBehaviour expects it to be present

Однако это не тот случай, если вы просто передаете несвязанный модуль, который не реализует поведение:

iex> ExpectBehaviour.use_behaviour(DoesNotImplementOrClaimTo)
** (UndefinedFunctionError) function DoesNotImplementOrClaimTo.foo/0 is undefined or private

Есть ли способ использовать поведение как тип вtypespecs?

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

...