Erlang basics · Lesson 08 (open contents)
01 · Open erl02 · Meet common terms03 · Tell two kinds of text apart04 · Bind a variable once05 · Split and filter a list06 · Parse a number safely07 · Choose one path with case08 · Let a function keep going09 · Put code in a module10 · Compare terms and Boolean results11 · Read and update nested maps12 · Build and inspect UTF-8 binaries13 · Let clauses choose the function path14 · Solve a list twice15 · Choose with patterns, guards, and errors16 · Describe a module and one record17 · Put one tested module in Rebar318 · Return success or failure as data19 · Read and write one file safely20 · Build a data pipeline with functions21 · Keep records behind a module boundary22 · Define a module contract23 · Describe the data contract24 · Test small rules and whole flows25 · Shape builds and make a command26 · Write the project promise27 · Parse one real line28 · Keep the public API small29 · Make the promise executable30 · Package the edge and leave clues31 · Prove the project is done
ERLANG · SCRATCH · LESSON 0835 minutes

Let a function keep going

Stop at an empty list and use a head-and-tail pattern for the next item.

01 · Start with the whole example

Copy the whole block into the erl shell

Read the [] clause first, then the [Head | Tail] clause. Do not try to follow every call at once.

Erlang
%% Clear variables left in the shell by earlier lessons
f().

%% Double stores the function; Loop is only for recursion inside it
Double =
  fun Loop([]) ->
        [];
      Loop([Head | Tail]) ->
        [Head * 2 | Loop(Tail)]
  end.

%% Call the function with its outside name
Double([1, 2, 3]).
Check the result first
  • Double([1, 2, 3]) returns [2, 4, 6].
  • The list becomes one item shorter with each call.
  • When the input reaches Loop([]), it returns [] and the recursion stops.
02 · Take the code apart

Read from the first line down

  1. For the input [1, 2, 3], Head is 1 and Tail is [2, 3].
  2. This layer prepares 1 * 2. The remaining work becomes Loop([2, 3]).
  3. Loop is visible only inside this fun. Code outside calls the whole function through the variable Double.
  4. The semicolon says another fun clause follows. The last clause goes straight to end..
  5. At last, the empty-list clause returns []. The results from the earlier layers are then joined back together.
03 · Meet the new symbols

Symbols are not secret signs

fun Loop([]) ->

Create an anonymous function with the internal name Loop. Its first clause accepts an empty list.

Loop([Head | Tail]) ->

The second clause splits the current item from the remaining items.

[Head * 2 | Loop(Tail)]

Put in the current result, then let Loop handle the shorter Tail.

04 · Ideas inside the code

Match each name to its meaning

01

recursion

A function calls itself inside its own definition.

02

function clause

One entry point for a function. Each clause accepts a different shape of argument.

03

stopping condition

A case that does not call the function again. This example returns when it reaches an empty list.

05 · Make the idea clear

Why these forms are useful

Erlang often processes a list with recursion. A function handles the first item, then gives the remaining list to itself.

Recursion needs a stopping condition. For a list, the empty list [] is a natural finish line.

One function can have several clauses. Erlang searches from top to bottom for a clause with the right shape. Here, the first clause receives an empty list and the second receives [Head | Tail].

06 · Change it yourself

Close the answer and try

Complete AddOne so that it adds 1 to every number in a list.

Practice starting point
%% Clear old variables so you can practice again
f().

%% Keep the stopping condition as it is
AddOne =
  fun Loop([]) ->
        [];
      Loop([Head | Tail]) ->
        [____ | Loop(Tail)]
  end.

%% Call the function and check its result
AddOne([2, 3, 4]).

Target result: AddOne([2, 3, 4]) should return [3, 4, 5].

Stuck? Read one hint

The new value for the current item is Head + 1.

After you run it, see one answer
One answer
%% f() belongs in the shell, not in an .erl file
f().

%% AddOne is the outside name; Loop is the inside recursion name
AddOne =
  fun Loop([]) ->
        [];
      Loop([Head | Tail]) ->
        [Head + 1 | Loop(Tail)]
  end.

%% This should return [3,4,5]
AddOne([2, 3, 4]).
Think about it: Where does this recursion stop?

When the input becomes an empty list, Loop([]) -> [] returns directly and does not call itself again.

Take with you

Remember these three lines

  1. 1

    Recursion turns a large problem into a smaller problem of the same kind.

  2. 2

    Without the [] clause, this example reaches an empty list and raises function_clause. Recursion only runs forever when the problem does not get smaller.

  3. 3

    Find the stopping condition before reading the recursive clause.

Lesson completeRun the code once, then mark this lesson as done.