Recently I found that Elixir’s typespec can’t detect type mismatch of anonymous/captured functions.
For demonstration purpose, say I have things like:
@spec mmap([number], (number -> number)) :: [number]
def mmap([], _), do: []
def mmap([head | tail], f), do: [ f.(head) | mmap(tail, f)]
def run do
mmap([1, 2, 3, 4, 5], &Integer.to_charlist/1) # This is always wrong since it returns a list of charlist instead of list of number
end
But dialixir let the code pass:
...
[:elixir, :kernel, :logger, :stdlib]
PLT is up to date!
No :ignore_warnings opt specified in mix.exs and default does not exist.
Starting Dialyzer
[
check_plt: false,
init_plt: '/Users/tai/Projects/sandbox/fun_thu/type_example/_build/dev/dialyxir_erlang-21.1.1_elixir-1.7.4_deps-dev.plt',
files_rec: ['/Users/tai/Projects/sandbox/fun_thu/type_example/_build/dev/lib/type_example/ebin'],
warnings: [:unknown]
]
Total errors: 0, Skipped: 0
done in 0m1.02s
done (passed successfully)
I tried to replace the &Integer.to_charlist/1
to an anonymous function, or a local function with typespec, but still no luck.
Expect behavior
When in Erlang, if I got something similar:
-spec mmap([number()], fun((number()) -> number())) -> [number()].
mmap([], _) -> [];
mmap([Head | Tail], F) ->
[F(Head) | mmap(Tail, F)].
run() ->
mmap([1, 2, 3, 4, 5], fun(I) -> integer_to_list(I) end).
Dialyzer reports an error:
tp.erl:4: Invalid type specification for function tp:mmap/2. The success typing is ([1 | 2 | 3 | 4 | 5],fun((_) -> string())) -> [string()]
done in 0m0.13s
Environment
Eralng 21.1.1
Elixir 1.7.4-otp-21
macOS 10.14.1
Question(s)
I’m wondering if I like to dig into the issue and make Elixir/dialyxir works in this situation, where should I start? As for my understanding, Erlang’s dialyzer analyzes the beam binary by default, not the source code. So is this because Elixir didn’t provide some information that dialyzer need, or it’s dialyxir’s own issue? Thanks for any advice or hint!