100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Programming

spawn and Links

Understand how Erlang creates new processes with spawn and how links create bidirectional failure-propagation relationships between them.

Concurrency & OTPIntermediate9 min readJul 10, 2026
Analogies

Creating Processes with spawn

The spawn/1 (or spawn/3 for a module/function/args form) built-in function creates a brand-new process running the given function and immediately returns its process identifier, a Pid, to the caller. Critically, spawn by itself establishes no relationship between the new process and its creator — the BEAM VM does not enforce a parent-child hierarchy — so if the spawned process crashes, the process that spawned it is never automatically notified unless a link or monitor is explicitly set up.

🏏

Cricket analogy: A franchise like Mumbai Indians signing a new net bowler for practice doesn't require the head coach's constant supervision structure to be defined upfront — the player (process) exists and is assigned a squad number (Pid) the moment they're signed, before any reporting lines are set.

Calling link/1 on a Pid — or using spawn_link/1,3 to create and link a process atomically in one step — establishes a bidirectional relationship between the two processes. By default, if either linked process terminates with an abnormal exit reason (anything other than normal), an exit signal propagates to the other, which also terminates, receiving the same reason. This propagation is what allows a crash to cascade cleanly through a set of tightly coupled processes rather than leaving orphaned, half-broken state behind.

🏏

Cricket analogy: Two batsmen at the crease running between wickets are linked — if one gets run out attempting a risky single, it directly affects the partnership's fate, just as a linked Erlang process's abnormal exit propagates to terminate its partner.

erlang
-module(link_demo).
-export([start/0, trapping_parent/0]).

start() ->
    process_flag(trap_exit, true),
    Child = spawn_link(fun() ->
        timer:sleep(1000),
        exit(unexpected_failure)
    end),
    receive
        {'EXIT', Child, Reason} ->
            io:format("Child ~p exited: ~p~n", [Child, Reason])
    end.

trapping_parent() ->
    process_flag(trap_exit, true),
    {Pid, Ref} = spawn_monitor(fun() -> 1/0 end),
    receive
        {'DOWN', Ref, process, Pid, Reason} ->
            io:format("Monitored process ~p went down: ~p~n", [Pid, Reason])
    end.

trap_exit: Turning Crashes into Messages

Calling process_flag(trap_exit, true) changes how a process handles incoming exit signals from its links: instead of being killed by them, the process receives them as ordinary {'EXIT', Pid, Reason} tuples delivered to its mailbox, which it can then pattern-match on inside a normal receive. This is the exact mechanism OTP's supervisor behaviour is built on — a supervisor is, at its core, a process that links to its children and traps their exits so it can react programmatically instead of dying alongside them.

🏏

Cricket analogy: A vice-captain designated to formally receive and log every injury report from the physio, rather than being knocked out of the game themselves, mirrors process_flag(trap_exit, true) converting a lethal exit signal into a harmless message the process can inspect.

A process created with plain spawn (no link, no monitor) is completely invisible to failure detection — if it crashes, nothing else in the system is notified. This is a common source of silently 'lost' processes in early Erlang code; prefer spawn_link or spawn_monitor unless true fire-and-forget isolation is intended.

monitor/2 (or the atomic spawn_monitor/1,3) sets up a unidirectional watch on a process: the monitoring process is notified with a {'DOWN', Ref, process, Pid, Reason} message when the monitored process terminates, for any reason, but the monitoring process itself is never killed as a side effect. This makes monitor the safer choice when you simply need to know that something finished or crashed — for example, to time out a request or clean up a resource — without accepting the bidirectional, potentially lethal coupling that a link creates.

🏏

Cricket analogy: A team analyst watching a rival team's player performance stats without being on their playing roster is a one-way relationship — the analyst's own game isn't affected by the rival player's dismissal, just as monitor/2 gives notification without the lethal, bidirectional tie of a link.

Use spawn_monitor (or monitor/2 on an existing Pid) when you need to know a process died — for example, to reply to a waiting caller with an error — but you must not risk your own process being killed as a side effect. Reserve link/spawn_link for cases where the two processes' lifetimes should genuinely be tied together, such as a supervisor and its child.

  • spawn/1,3 creates a new process and immediately returns its Pid, with no default supervision relationship.
  • spawn_link/1,3 atomically creates and links a process, avoiding a race condition present in separate spawn+link calls.
  • Links are bidirectional: an abnormal exit on either linked process propagates an exit signal to the other.
  • By default, receiving a non-normal exit signal terminates the receiving process too.
  • process_flag(trap_exit, true) converts incoming exit signals into ordinary {'EXIT', Pid, Reason} messages.
  • monitor/2 (or spawn_monitor) creates a unidirectional, non-lethal notification via a 'DOWN' message.
  • Supervisors are built on top of spawn_link and trap_exit.

Practice what you learned

Was this page helpful?

Topics covered

#Programming#ErlangStudyNotes#SpawnAndLinks#Spawn#Links#Creating#Processes#StudyNotes#SkillVeris#ExamPrep