I noticed that if I pass a captured function (e.g. &logger/4) as an argument to :telemetry.attach/4, I get a warning:
[info] The function passed as a handler with ID “…” is a local function.
This means that it is either an anonymous function or a capture of a function without a module specified. That may cause a performance penalty when calling that handler. For more details see the note intelemetry:attach/4documentation.
telemetry — telemetry v1.2.1
… but if I change this to &__MODULE__.logger/4, I no longer get that warning. The link says:
Note: due to how anonymous functions are implemented in the Erlang VM, it is best to use function captures (i.e.
fun mod:fun/4in Erlang or&Mod.fun/4in Elixir) as event handlers to achieve maximum performance. In other words, avoid using literal anonymous functions (fun(...) -> ... endorfn ... -> ... end) or local function captures (fun handle_event/4or&handle_event/4) as event handlers.
Is there more documentation about what “due to how anonymous functions are implemented in the Erlang VM” is alluding to? I read the documentation for &/1 but it doesn’t go into detail about why &foo/n is different from &__MODULE__.foo/n, and I am curious!
The Erlang documentation on functions makes it sound like foo() and m:foo() should have the same performance (Functions — Erlang System Documentation v27.0):
This is a rough hierarchy of the performance of the different types of function calls:
- Calls to local or external functions (
foo(),m:foo()) are the fastest calls.- Calling or applying a fun (
Fun(),apply(Fun, [])) is just a little slower than external calls.- Applying an exported function (
Mod:Name(),apply(Mod, Name, [])) where the number of arguments is known at compile time is next.- Applying an exported function (
apply(Mod, Name, Args)) where the number of arguments is not known at compile time is the least efficient.
Are local function captures (without the module name) compiled into Erlang Fun()s instead of local/external functions? If so, why?




















