iex(4)> f=&Foo.bar/1
&Foo.bar/1
iex(5)> f.(1)
** (UndefinedFunctionError) function Foo.bar/1 is undefined (module Foo is not available)
Foo.bar(1)
iex:5: (file)
iex(5)> defmodule Foo do
...(5)> def bar(a) do
...(5)> a+a
...(5)> end
...(5)> end
{:module, Foo, ...}
iex(6)> f.(1)
2
Given modules on the beam can become available at any time it’s not necessarily wrong to capture a function for a module, which doesn’t exist/is not compiled. If you need a check consider function_exported?/3.
I think the correct thing is to use the is_function/2 guard, which will ensure that the captured arity is correct.
defmodule Example do
def example(fun) when is_function(fun, 1) do
:ok
end
end
iex> Example.example(&(&1 + 1))
:ok
iex> Example.example(&Foo.bar/1)
:ok
iex> Example.example(&Foo.bar/2)
** (FunctionClauseError) no function clause matching Test.example/1
This will give you a function clause error if the wrong arity is captured, or an UndefinedFunctionError if the right arity is there but the function doesn’t exist or isn’t exported (like Foo.Bar/1).
To clarify, the UndefinedFunctionError will only happen when you invoke it, because as others have pointed, the function can be defined at any moment.
However, note that the Elixir Mix compiler will warn in such cases. It collects all function references and warns once it gathered all modules and invocations.