Why doesn't Elixir check the arity of anonymous function calls?

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?

Because the compiler doesn’t know that bar_fns type is a function at all. How should it then know and check it’s arity?

Try it out and assign 4, the compiler will still not complain.

1 Like

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.

As I said, the compiler doesn’t know about types.

f = 4
f.(1)

This would compile as well.

1 Like

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.

3 Likes

Indeed, neither the compiler nor Dialyzer detects this error.

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.

1 Like