How to interpret a function attribute in FunctionClauseError
%FunctionClauseError{
module: MyApp.MyModule,
function: :"-load_data/4-inlined-0-",
arity: 1,
kind: nil,
args: nil,
clauses: nil
}
The term -load_data/4-inlined-0-
points to something inside the load_data/4
function.
How to interpret this part: inlined-0
?
This part of the environment seems to be undocumented. Can anyone elaborate on this subject?
The -load_data/4-inlined-0-
function name occurs when an anonymous function is inlined by the compiler, and the call to the anonymous function fails with a function clause error. It is not the name of an actual function; it was added to fix this issue, titled "Confusing case_clause
exception instead of function_clause
". I don't think this is documented anywhere.
This requires a little history lesson. Let's consider this piece of code:
-module(foo).
-export([foo/0]).
foo() ->
F = fun(X) when is_atom(X) -> ok end,
F(0).
That is, we create an anonymous function, and then we call it in a way that causes a function clause error.
Inlining of anonymous functions was introduced in Erlang/OTP 24. Thus, when running this in Erlang 23, we can see that the function name in the stack trace is the usual name for anonymous functions, with -fun-
:
> catch foo:foo().
{'EXIT',{function_clause,[{foo,'-foo/0-fun-0-',
[0],
[{file,"foo.erl"},{line,6}]},
{erl_eval,do_apply,6,[{file,"erl_eval.erl"},{line,684}]},
{erl_eval,expr,5,[{file,"erl_eval.erl"},{line,437}]},
{shell,exprs,7,[{file,"shell.erl"},{line,686}]},
{shell,eval_exprs,7,[{file,"shell.erl"},{line,642}]},
{shell,eval_loop,3,[{file,"shell.erl"},{line,627}]}]}}
In Erlang 24, the function is inlined by the compiler, and gets rewritten to a case
expression. Therefore, we see a case_clause
exception, and the stack trace suggests that the error occurred in the foo
function itself:
> catch foo:foo().
{'EXIT',{{case_clause,{0}},
[{foo,foo,0,[{file,"foo.erl"},{line,6}]},
{erl_eval,do_apply,6,[{file,"erl_eval.erl"},{line,689}]},
{erl_eval,expr,5,[{file,"erl_eval.erl"},{line,434}]},
{shell,exprs,7,[{file,"shell.erl"},{line,686}]},
{shell,eval_exprs,7,[{file,"shell.erl"},{line,642}]},
{shell,eval_loop,3,[{file,"shell.erl"},{line,627}]}]}}
Of course, that's confusing for a programmer who goes looking for a case
expression to find the cause of the error, and doesn't find any. That's why Erlang 25 makes sure to use an actual function_clause
exception, to reflect the fact that the error occurred at a place where there was a function call in the code. It uses -inlined-
instead of -fun-
since the anonymous function is no longer present in the compiled module:
> catch foo:foo().
{'EXIT',{function_clause,[{foo,'-foo/0-inlined-0-',
[0],
[{file,"foo.erl"},{line,6}]},
{erl_eval,do_apply,7,[{file,"erl_eval.erl"},{line,748}]},
{erl_eval,expr,6,[{file,"erl_eval.erl"},{line,480}]},
{shell,exprs,7,[{file,"shell.erl"},{line,691}]},
{shell,eval_exprs,7,[{file,"shell.erl"},{line,647}]},
{shell,eval_loop,3,[{file,"shell.erl"},{line,632}]}]}}