I recently detected a bug (thanks, Dialyzer!) where I was calling an anonymous function with the wrong number of arguments. What confuses me is the fact that the Elixir compiler didn’t complain. Here is a sample which demonstrates the issue:
defp foo(c) do
bar_fn = fn a, b -> a + b end
bar_fn.(c)
end
Is there some reason why this check is not being made?
By making the call bar_fn.(c), my code is implicitly asserting that bar_fn is an anonymous function. If bar_fn had been passed in as a parameter, checking its arity would require something like Dialyzer.
However, in this case, bar_fn is being defined in the current function. So, all of the needed information should be available to perform type inference (or whatever). I’m not saying it would be easy (I really don’t know), but it seems like it should be possible.
Elixir is dynamically typed which means that the code analysis the compiler does is limited. The same issue is can be seen with code such as x = :abc; x + 1. A statically type checked language would find issues like this.
One way you could get around this is to pass the anonymous function to another function and put a typespec in place that asserts on the anonymous function’s signature:
defmodule Demo do
@spec do_stuff(atom(), (integer(), integer() -> integer())) :: atom()
def do_stuff(key, anon_fn) when is_atom(key) and is_function(anon_fn) do
anon_fn.(2, 3)
:ok
end
@spec call_do_stuff() :: atom()
def call_do_stuff() do
anon_fn = fn a, b -> a + b end
do_stuff(:hello, anon_fn)
end
end
This is just quickly scribbled without compiling but at least that way you can rely on Dialyzer, part of the time.