elixir - 如何在行为中使用 typespecs 和 Dialyzer?

标签 elixir

在 Elixir 中,我如何记录一个函数将返回一个实现特定行为的模块?

举一个简单的例子,假设我创建了一个 GreeterBehaviour由两个模块实现的行为:

defmodule GreeterBehaviour do
  @callback say_hello(String.t) :: String.t
end

defmodule FormalGreeter do
  @behaviour GreeterBehaviour

  def say_hello(name) do
    "Good day to you #{name}"
  end
end

defmodule CasualGreeter do
  @behaviour GreeterBehaviour

  def say_hello(name) do
    "Hey #{name}"
  end
end

然后,我想通过一个函数检索 Greeter 来轻松地换出这些实现中的任何一个:
defmodule MyApp do
  def main do
    greeter().say_hello("Pete") |> IO.puts
  end

  @spec greeter() :: GreeterBehaviour # This doesn't work with dialyzer
  def greeter do
    FormalGreeter # Can easily be swapped to CasualGreeter
  end
end

Dialyzer 将成功检查 CasualGreeterFormalGreeter正确实现 GreeterBehaviour行为。但是,如何定义类型规范以便 Dialyzer 将检查 greeter/0返回一个实际上实现了 GreeterBehaviour 的模块?

使用 @spec greeter() :: GreeterBehaviour不起作用,因为 Dialyzer 会发出警告:
lib/my_app.ex:19: Invalid type specification for function 'Elixir.MyApp':greeter/0. The success typing is () -> 'Elixir.FormalGreeter'

最佳答案

在您的行为中,您可以为 say_hello 定义一个类型:

@type f :: (String.t() -> String.t())

您的 greeter函数可以返回一个模块 + 函数:&module.say_hello/1
规范是:
@spec greeter() :: GreeterBehaviour.f()
defmodule GreeterBehaviour do
  @type f :: (String.t() -> String.t())
  @callback say_hello(String.t()) :: String.t()
end

defmodule FormalGreeter do
  @behaviour GreeterBehaviour

  def say_hello(name) do
    "Good day to you #{name}"
  end
end

defmodule CasualGreeter do
  @behaviour GreeterBehaviour

  def say_hello(name) do
    "Hey #{name}"
  end
end

defmodule MyApp do
  def main do
    greeter().("Pete") |> IO.puts()
  end

  # This will work with dialyzer
  @spec greeter() :: GreeterBehaviour.f()
  def greeter do
    # Can easily be swapped to CasualGreeter
    &FormalGreeter.say_hello/1
  end
end

关于elixir - 如何在行为中使用 typespecs 和 Dialyzer?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44731975/

相关文章:

elixir - Elixir 中是否有相当于 Python 的 dir() 的函数

elixir - 如何通过 Elixir 中的键来理解从 map 中获取值(value)

erlang - rabbitmq 处理大量消费者、连接与 channel

pattern-matching - 模式匹配中 byte_size 的语法是什么?

elixir - JSON 响应的单元测试,与 JSON 响应匹配以断言键和值

deployment - Elixir:使用 edeliver 构建和部署伞形应用

elixir - Elixir 在函数子句中匹配相同值的方式是什么?

hex - 如何使用 Elixir 打包/解包十六进制字符串(先是高半字节)

elixir - 直接从 Phoenix 路由器重定向

elixir - 使用 HTTPoison 的 HTTP 1.0 请求