Prev Next

Erlang / Erlang Advanced Interview questions

What is tail call optimization in Erlang and why does it matter for long-running loops?

A function call is a tail call when it's the very last operation in a clause — nothing happens with its result except returning it directly. Erlang recognizes this pattern and reuses the current stack frame instead of pushing a new one, so a tail-recursive function can loop indefinitely without growing the call stack.

%% tail-recursive: the recursive call is the last thing evaluated
loop(State) ->
    receive
        Msg -> loop(handle(Msg, State))   %% tail call
    end.

%% NOT tail-recursive: work happens AFTER the recursive call returns
sum([]) -> 0;
sum([H|T]) -> H + sum(T).                 %% addition happens after the call

This matters directly for OTP: every gen_server, supervisor, and hand-rolled process loop relies on tail call optimization to run forever inside a receive loop without ever running out of stack space. Without it, a long-lived Erlang process would eventually overflow its stack simply by staying alive and handling messages.

What makes a function call a 'tail call'?
Why is tail call optimization essential for a gen_server's message loop?

More Related questions...

What is the difference between linking and monitoring a process? Why would you choose monitor over link for a client process? What is a dirty scheduler and when should a NIF use one? Explain the internal working of Erlang's per-process garbage collector? Why does Erlang use generational (per-process) garbage collection instead of a single global GC? What is EPMD and what role does it play in distributed Erlang? Why do distributed Erlang nodes require a shared cookie? Explain the internal working of the Erlang distribution handshake between two nodes? What is the two-version code loading rule and what happens when a third version is loaded? Explain the internal working of code:purge/1 and code:soft_purge/1? What is a gen_event and when would you choose it over gen_server? What is the difference between error, exit, and throw in Erlang? Why is exit/1 different from exit/2? What is an OTP application (.app file) and how does it differ from a single module? Explain the execution flow of application:start/1 and its dependency resolution? What is a release in OTP and how does it differ from an application? Explain the internal working of a relup-based hot upgrade? What is the code_change/3 callback used for in gen_server? How does the global module handle process name registration across a cluster? Why can global name registration cause a network partition ("split brain") problem? What is the pg (process groups) module used for? What are Erlang maps and how do they differ from records? When should you choose a map over a record for structured data? What is the difference between ets:match, ets:select, and ets:foldl? How can you use match specifications to filter ETS data efficiently? What do the write_concurrency and read_concurrency ETS options optimize for? How can you optimize binary pattern matching for parsing variable-length network protocols? Why is copying a large list between processes more expensive than copying a large binary? What is tail call optimization in Erlang and why does it matter for long-running loops? How do you write a properly tail-recursive accumulator-based function? Explain the internal working of selective receive and why message order in the queue matters? What is erlang:process_flag(priority,...) used for? Why should high-priority processes be used sparingly in Erlang? What is rpc:call/4 and how does it work across distributed nodes? What is the difference between rpc:call and simply sending a message to a remote PID? How does erlang:send/3 with the nosuspend option change message-sending behavior? What is Dialyzer and how does success typing differ from static typing? Why doesn't Dialyzer catch every type error the way a traditional type checker would? How do you define and use a custom behavior with the -callback attribute? What is the difference between a behavior callback module and a plain library module? Explain the internal working of exception propagation through nested try/catch blocks? When should you use throw instead of returning an {error, Reason} tuple? How do you troubleshoot a memory leak caused by large binaries not being garbage collected? What is the significance of the binary reference count and off-heap binary garbage collection? Why is the Erlang distribution protocol not encrypted by default, and how can you secure inter-node traffic? Why is Mnesia's two-phase commit necessary for distributed transactions? How do you troubleshoot slow ETS lookups on a heavily-used table? Why might increasing the number of BEAM schedulers not improve throughput linearly?
Show more question and Answers...

AI

Comments & Discussions