How do you properly use a typed list spec? Normally it would display a “breaks the contract” warning when using wrong type but this doesnt seem to work for a typed list.
I have a method which receives a list of strings, so I defined this kind of spec:
@spec check?([String.t]) :: boolean
But then, I noticed I can tdefine any spec for the list and it will never complain when running a dialyzer, i.e:
Can you please also show the implementation of that function, as well as an example call, since both are analyzed separately and therefore two cases where a warning might occur or not occur.
These are two functions, one receives a list of Strings and other receives just a String, both do the same.
As you can see both have wrong specs, defining number type paramter.
But when running the dialyzer I just receive a warning for the second method:
Invalid type specification for function ‘Elixir.Spec’:remove_host/1. The success typing is (binary
defmodule Spec do
@spec remove_host_list(nonempty_list(number)) :: String.t
def remove_host_list(url_list) do
Enum.find_value(url_list || [], false, fn(url) ->
url
|> URI.parse
|> Map.put(:host, nil)
|> URI.to_string
end)
end
@spec remove_host(number) :: String.t
def remove_host(url) do
url
|> URI.parse
|> Map.put(:host, nil)
|> URI.to_string
end
end
If I run the remove_host_list method with a list of numbers like:
iex(16)> Spec.remove_host_list([1, 2, 3])
It crash:
The following arguments were given to URI.parse/1:
# 1
1
But it works for strings obviously:
Spec.remove_host_list([“1”, “2”, “3”])
“1”
So why this spec doesnt display a warning? @spec remove_host_list(nonempty_list(number)) :: String.t
Here it works for dialyzer because the Enumerables functions all do accept Enum.t which is any. Therefore the URL.parse type does not bubble up. If though you annotate it as list of string.t but pass in a list of ints dialyzer should barf at you.
Also the warning for the second is as expected and tells you that you should use binary instead of number. string.t is just an alias for binary.