Erlang from the ground up

Your first Erlang

What an Erlang file actually is: a module, an exported entry point called main/1, and io:format writing the answers out.

Part 1 of 12

Beginner14 min readBack to Courses

A file is a module

An Erlang file is not a script. It defines a module — a named collection of functions — opened with -module(prog). and closed by the full stop at the end of that line. The name in the line has to match the file name, which is why the file here is prog.erl and the module is prog.

A module chooses what the outside world is allowed to call. -export([main/1]). says that a function called main taking one argument may be called from outside. The 1 is not a value, it is the arity: how many arguments the function takes. main/1 and main/0 are two different functions as far as Erlang is concerned, and the runner here calls the one with an argument.

That argument arrives in a variable. A variable starts with a capital letter, and a lone underscore _ means a value that is there but deliberately ignored. Since nothing is passed in, the entry point is written main(_) -> and the value is dropped.

Two mistakes look like a missing function but are not. A module line with a capital letter is a syntax error, because module names are atoms and an atom that is not quoted starts lowercase. And a file saved as prog.erl.txt still opens in an editor, but the compiler is handed a name ending in .txt and the module check fails before the first function is compiled.

Saying something

io:format writes text. Its first argument is a format string and its second is a list of values to drop into it. ~p prints a value in raw form, ~s prints a string as plain text, and ~n ends the line. Without ~n, the next write carries on where the last one stopped.

Inside a clause the expressions are separated by commas, and the clause itself ends with a full stop. A comment starts with a percent sign and runs to the end of the line. Every function lives inside a module, so even a five-line program is a module with one exported function.

io:format returns the atom ok, and the comma after each call is there because the clause is a sequence: each expression is evaluated for its effect and the next one follows. The last expression before the full stop is the value the function returns, so main/1 answers whatever helper() answers, which is ok as well.

The smallest complete program

-module(prog).
-export([main/1]).

main(_) ->
    io:format("hello~n"),
    io:format("six times seven is ~p~n", [6 * 7]),
    io:format("a string is ~s~n", ["text"]),
    io:format("the same string ~p~n", ["text"]),
    helper().

helper() ->
    io:format("a helper answered~n").

Run it, then make it yours

Press Run and read the output line by line. Then change the sum, add a line of your own with `~s` in it, and change the helper to print a different sentence.

Output
Click "Run" to execute your code

When a run complains about main, check three things in order: the file is called prog.erl, the module line says prog, and the export lists main/1. Those three lines are the whole contract the runner asks of a program.

The name is the address

The name on the module line is not a label for the file; it is the address the runtime uses when another module calls a function. lists:sort/1 means the function sort taking one argument inside the module lists, and a call to your own module is written the same way: prog:main(). If the module line and the file name disagree, the compiler refuses the file before any function runs.

A module is compiled, not read line by line. The compiler checks every form in the file, agrees each function name with an arity, and writes a .beam file. That is why a mistake in a function you never call can still stop the run: the whole file has to compile before the first line executes.

The entry point, seen from outside

The export list is the only public door. A function that is not exported can still be called by another function in the same module, which is how a small helper stays private, but nothing outside the module can reach it.

Arity is part of the name, so main/0 and main/1 are two different functions. A module can offer both, with one forwarding to the other. That is how a program accepts a list of arguments from a shell and still has a plain no-argument entry point for a page that runs it.

Three failures account for most first Erlang files: a module line that does not match the file name, a missing full stop at the end of a function (a semicolon only separates clauses), and a call to a function whose name and arity are not exported. Each one is a compile error, so read the first message before changing anything else.

Try these next

  • Give the program a second private helper and call it twice from main.
  • Print the same string once with ~s and once with ~p, and write down why the quotes differ.
  • Add a comment above the module line, then remove it and confirm the program still runs.

A module, a public entry and a private helper

Run it and trace which lines come from `main` and which come from the helper. Then add a third item and print the total with two decimals.

Output
Click "Run" to execute your code

Check what you learned

One question at a time. A wrong answer explains itself — take another go.

Part 1 of 5

What does -export([main/1]). say?