First, I don't see any elixir typespec tutorials where they write the @spec with the variable names in the spec--instead all I find are tutorials with types only in the typespec:
@spec calculate(arg1 :: String.t, arg2 :: CustomType.t, arg3 :: String.t)
v.
@spec calculate(String.t, CustomType.t, String.t)
Nevertheless, the following passed dialyzer for me:
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)
I will say that I find this syntax:
@spec calculate(String.t, CustomType.t, String.t)
much easier to read.
According to Learn You Some Erlang:
is a subtype of the success typing
This warns you that in fact, your specification is way too strict for
what your code is expected to accept, and tells you (albeit
indirectly) that you should either make your type specification
looser, or validate your inputs and outputs better in your functions
to reflect the type specification.
However, I am unable to produce your dialyzer output:
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)
So, you need to post a minimal example that reproduces your dialyzer output. I will note that arg3 has to be a binary in your first clause, so when you call calculate(arg1, arg2, [arg3])
in the first clause's body, the argument [arg3]
will never be an improper list, so you can tighten that spec up to: list(binary)
for the second clause.
============
Here's the code I've put together:
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
Running dialyzer:
~/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)
With the following in 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
Here's the output:
~/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)