我具有以下两个功能,并且收到有关它们的透析器警告:
"Type specification 'Elixir.MyModule':calculate(arg1::'Elixir.String':t(),arg2::'Elixir.CustomType':t(),arg3::'Elixir.String':t()) -> 'ok' | 'error'; calculate(arg1::'Elixir.String':t(),arg2::'Elixir.CustomType':t(),arg3::maybe_improper_list()) -> 'ok' | 'error' is a subtype of the success typing: 'Elixir.MyModule':calculate(binary(),#{'__struct__':='Elixir.CustomType', _=>_},binary() | maybe_improper_list()) -> 'error' | 'ok'"
以下是功能:
@spec calculate(arg1 :: String.t, arg2 :: CustomType.t, arg3 :: String.t)
def calculate(arg1, %CustomType{} = arg2, arg3) when is_binary(arg1) and is_binary(arg3) do
calculate(arg1, arg2, [arg3])
end
@spec calculate(arg1 :: String.t, arg2 :: CustomType.t, arg3 :: maybe_improper_list())
def calculate(arg1, %CustomType{prop1: val, prop2: val2}, arg3) when is_binary(arg1) and is_integer(val2) and is_binary(val) and is_list(arg3) do
...
end
我不明白为什么收到此警告。我以为这是在Elixir中编写具有不同参数类型的函数的正确方法,但是鉴于Dialyzer不断发出警告,我开始怀疑我是否在错误地编写此代码?
defmodule CustomType do
@type t :: %CustomType{
prop1: String.t(),
prop2: integer(),
prop3: String.t(),
prop4: boolean(),
...
}
end
这些是我正在运行的透析器标志:
dialyzer: [
flags: ~w[underspecs overspecs race_conditions error_handling unmatched_returns]a
]
复制样本:
defmodule MyCustomType do
@type t :: %MyCustomType{
prop1: String.t(),
prop2: integer(),
prop3: String.t()
}
defstruct [:prop1, :prop2, :prop3]
end
defmodule MyModule do
@spec calculate(String.t,
MyCustomType.t,
String.t)
:: :ok
def calculate(arg1, %MyCustomType{} = arg2, arg3) when is_binary(arg1) and is_binary(arg3) do
calculate(arg1, arg2, [arg3])
end
@spec calculate(String.t,
MyCustomType.t,
maybe_improper_list)
:: :ok
def calculate(arg1, %MyCustomType{prop1: val, prop2: val2}, arg3) when is_binary(arg1) and is_list(arg3) and is_binary(val) and is_integer(val2) do
:ok
end
end
以下是我收到的警告:
Type specification 'Elixir.MyModule':calculate
('Elixir.String':t(),
'Elixir.MyCustomType':t(),
'Elixir.String':t()) ->
'ok';
('Elixir.String':t(),
'Elixir.MyCustomType':t(),
maybe_improper_list()) ->
'ok' is a subtype of the success typing: 'Elixir.MyModule':calculate
(binary(),
#{'__struct__' := 'Elixir.MyCustomType',
'prop1' := binary(),
'prop2' := integer(),
_ => _},
binary() | maybe_improper_list()) ->
'ok'
答案 0 :(得分:1)
首先,我看不到任何elixir typespec教程,在这些教程中他们在规范中使用变量名编写@spec;相反,我发现的都是在typespec中仅具有类型的教程:
@spec calculate(arg1 :: String.t, arg2 :: CustomType.t, arg3 :: String.t)
v.
@spec calculate(String.t, CustomType.t, String.t)
尽管如此,以下内容还是通过了我的透析器:
defmodule CustomType do
@type t :: %CustomType{}
defstruct a: nil, b: nil
end
defmodule MyModule do
@spec calculate(arg1::String.t, arg2::CustomType.t, arg3::String.t) :: number
#def calculate(<<arg1::binary>>, %CustomType{} = arg2, <<arg3::binary>>) do
def calculate(arg1, %CustomType{} = arg2, arg3) when is_binary(arg1) and is_binary(arg3) do
calculate(arg1, arg2, [arg3])
end
@spec calculate(String.t, CustomType.t, maybe_improper_list()) :: number
def calculate(<<arg1::binary>>, %CustomType{} = arg2, arg3) when is_list(arg3) do
123
end
end
~/elixir_programs/friends$ mix dialyzer
Compiling 1 file (.ex)
warning: variable "arg1" is unused (if the variable is not meant to be used, prefix it with an underscore)
lib/friends/my_module.ex:17
warning: variable "arg2" is unused (if the variable is not meant to be used, prefix it with an underscore)
lib/friends/my_module.ex:17
Checking PLT...
[:compiler, :connection, :crypto, :db_connection, :decimal, :ecto, :elixir,
:kernel, :logger, :poolboy, :postgrex, :stdlib]
PLT is up to date!
Starting Dialyzer
dialyzer args: [
check_plt: false,
init_plt: '/Users/7stud/elixir_programs/friends/_build/dev/dialyxir_erlang-20.3_elixir-1.8.2_deps-dev.plt',
files_rec: ['/Users/7stud/elixir_programs/friends/_build/dev/lib/friends/ebin'],
warnings: [:unknown]
]
done in 0m1.43s
done (passed successfully)
我会说我找到这种语法:
@spec calculate(String.t, CustomType.t, String.t)
更容易阅读。
is a subtype of the success typing
这会警告您,实际上,您的规范对于 您的代码应接受什么并告诉您(尽管 间接)您应该做出类型说明 放松,或在功能上更好地验证您的输入和输出 反映类型规范。
但是,我无法生成您的透析器输出:
defmodule CustomType do
@type t :: %CustomType{}
defstruct a: nil, b: nil
end
defmodule MyModule do
@spec calculate(arg1 :: String.t,
arg2 :: CustomType.t,
arg3 :: String.t)
:: :ok | :error
def calculate(arg1, %CustomType{} = arg2, arg3)
when is_binary(arg1) and is_binary(arg3) do
calculate(arg1, arg2, [arg3])
end
@spec calculate(arg1 :: String.t,
arg2 :: CustomType.t,
arg3 :: maybe_improper_list())
:: :ok | :error
def calculate(arg1, %CustomType{} = arg2, arg3)
when is_binary(arg1) and is_list(arg3) do
case arg1 do
"hello" -> :ok
"goodbye" -> :error
end
end
end
$ mix dialyzer
Compiling 1 file (.ex)
warning: variable "arg2" is unused (if the variable is not meant to be used, prefix it with an underscore)
lib/friends/my_module.ex:19
Checking PLT...
[:compiler, :connection, :crypto, :db_connection, :decimal, :ecto, :elixir,
:kernel, :logger, :poolboy, :postgrex, :stdlib]
PLT is up to date!
Starting Dialyzer
dialyzer args: [
check_plt: false,
init_plt: '/Users/7stud/elixir_programs/friends/_build/dev/dialyxir_erlang-20.3_elixir-1.8.2_deps-dev.plt',
files_rec: ['/Users/7stud/elixir_programs/friends/_build/dev/lib/friends/ebin'],
warnings: [:unknown]
]
done in 0m1.46s
done (passed successfully)
因此,您需要发布一个最小的示例来复制您的透析器输出。我将注意到arg3必须在第一个子句中为二进制,因此当您在第一个子句的正文中调用calculate(arg1, arg2, [arg3])
时,参数[arg3]
永远不会是不正确的列表,因此您可以收紧该规范直到:list(binary)
(第二个子句)。
============
这是我编写的代码:
defmodule CustomType do
@type t :: %CustomType {
prop1: String.t(),
prop2: integer(),
prop3: String.t(),
prop4: boolean()
}
defstruct prop1: nil, prop2: nil, prop3: nil, prop4: nil
end
defmodule MyModule do
@spec calculate(arg1 :: String.t,
arg2 :: CustomType.t,
arg3 :: String.t)
:: :ok | :error
def calculate(arg1, %CustomType{} = arg2, arg3)
when is_binary(arg1) and is_binary(arg3) do
calculate(arg1, arg2, [arg3])
end
@spec calculate(arg1 :: String.t,
arg2 :: CustomType.t,
arg3 :: maybe_improper_list)
:: :ok | :error
def calculate(arg1, %CustomType{prop1: val, prop2: val2}, arg3)
when is_binary(arg1) and is_binary(val) and is_integer(val2) and is_list(arg3) do
case arg1 do
"hello" -> :ok
"goodbye" -> :error
end
end
end
正在运行的透析器:
~/elixir_programs/friends$ mix dialyzer
Checking PLT... [:artificery,
:compiler, :connection, :crypto, :db_connection, :decimal, :distillery,
:ecto, :elixir, :kernel, :logger, :poolboy, :postgrex, :runtime_tools,
:stdlib] Finding suitable PLTs Looking up modules in dialyxir_erlang-
20.3_elixir-1.8.2_deps-dev.plt Finding applications for dialyxir_erlang-
20.3_elixir-1.8.2_deps-dev.plt Finding modules for dialyxir_erlang-
20.3_elixir-1.8.2_deps-dev.plt Checking 718 modules in dialyxir_erlang-
20.3_elixir-1.8.2_deps-dev.plt Adding 56 modules to dialyxir_erlang-
20.3_elixir-1.8.2_deps-dev.plt Starting Dialyzer dialyzer args: [
check_plt: false,
init_plt: '/Users/7stud/elixir_programs/friends/_build/dev/dialyxir_erlang-
20.3_elixir-1.8.2_deps-dev.plt', files_rec:
['/Users/7stud/elixir_programs/friends/_build/dev/lib/friends/ebin'],
warnings: [:unknown] ] done in 0m1.26s done (passed successfully)
mix.exs中包含以下内容:
def project do
[
app: :friends,
version: "0.1.0",
elixir: "~> 1.6",
start_permanent: Mix.env() == :prod,
deps: deps(),
dialyzer: [
flags: ~w[underspecs
overspecs
race_conditions
error_handling
unmatched_returns]a
]
]
end
以下是输出:
~/elixir_programs/friends$ mix dialyzer
Checking PLT...
[:artificery, :compiler, :connection, :crypto, :db_connection, :decimal,
:distillery, :ecto, :elixir, :kernel, :logger, :poolboy, :postgrex,
:runtime_tools, :stdlib]
PLT is up to date!
Starting Dialyzer
dialyzer args: [
check_plt: false,
init_plt: '/Users/7stud/elixir_programs/friends/_build/dev/dialyxir_erlang-20.3_elixir-1.8.2_deps-dev.plt',
files_rec: ['/Users/7stud/elixir_programs/friends/_build/dev/lib/friends/ebin'],
warnings: [:underspecs, :overspecs, :race_conditions, :error_handling,
:unmatched_returns, :unknown]
]
done in 0m1.38s
done (passed successfully)