检查模块是否实现了行为

Bre*_*tty 8 elixir

我有一个行为和一个函数,它采用应该实现该行为的模块列表。我想检查传入的每个模块是否确实实现了该行为。我可以用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
Run Code Online (Sandbox Code Playgroud)

这是检查的最好方法吗?我在文档或 Elixir 论坛或任何地方都找不到任何内容。

我应该检查一下吗?或者我应该让责任完全落在来电者身上?行为是否更多是关于“我想确保我实现所有需要的东西”而不是“我希望其他人知道我实现了所有需要的东西”?

有没有办法在 typespecs 中使用行为作为类型?我的函数规范可以说 args 应该实现我的行为,还是应该只使用module()/atom()

Ada*_*hip 6

有趣的问题。

行为是否更多是关于“我想确保我实现所有需要的东西”而不是“我希望其他人知道我实现了所有需要的东西”?

我的理解是,行为是模块作者和该模块的用户之间的契约:“我希望你为我提供一个可以做所有这些事情的模块”。所以这是模块用户的责任。

@callback我看来,行为函数的关键字似乎是说,定义行为的模块通常也是将使用该行为的模块(换句话说,调用回调)。行为实现者似乎有责任确保它正确实现行为,并通过编译时检查来帮助他们解决问题,但是对于需要行为的模块的用户来说,没有运行时帮助确保他们实际上提供了有效的实现。

您提供运行时警告的解决方案对我来说看起来不错 - 但是可以在不提供@behaviour属性的情况下实现行为,因此在这种情况下它不会很好地工作。

如果行为实现者@behaviour在他们的代码中声明但忽略了编译器警告,则会有一个稍微有用的错误消息:

警告:未实现行为 ExpectBehaviour 所需的函数 foo/0(在模块 ClaimsItImplementsButDoesNot 中)

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

但是,如果您只是传递一个不实现该行为的不相关模块,则情况并非如此:

iex> ExpectBehaviour.use_behaviour(DoesNotImplementOrClaimTo)
** (UndefinedFunctionError) function DoesNotImplementOrClaimTo.foo/0 is undefined or private
Run Code Online (Sandbox Code Playgroud)

有没有办法在 typespecs 中使用行为作为类型?

行为不是类型,它是一组功能的规范,一个模块可以实现多种行为,所以我认为这没有意义。如上所述,将行为回调的使用限制在定义它的模块中似乎是明智的。