在 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 将成功检查
CasualGreeter
和 FormalGreeter
正确实现 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/