Lesson 1: Your First Erlang Module

Learn Erlang's basic syntax, modules, functions, and data types by building utility functions for our chat server

Edit on GitHub

Your First Erlang Module

Welcome back! Now that you have Erlang installed and your project set up, it’s time to write your first Erlang code. In this post, we’ll cover the fundamentals that make Erlang unique and start building components for our chat server.

What Makes Erlang Special?

Before diving into syntax, understand these core principles:

  1. Everything is immutable - Once created, values never change
  2. Functions return values - Don’t modify state, just return results
  3. Pattern matching - We’ll explore this powerful feature in lesson 2
  4. Processes communicate by message passing - No shared memory

These principles make Erlang perfect for building fault-tolerant, concurrent systems.


Module Basics

In Erlang, all code lives in modules. Let’s create our first module with utilities for our chat server:

Create src/chat_utils.erl:

-module(chat_utils).
-export([format_message/2, current_timestamp/0]).
format_message(User, Message) ->
Timestamp = current_timestamp(),
"[" ++ Timestamp ++ "] " ++ User ++ ": " ++ Message.
current_timestamp() ->
{{Year, Month, Day}, {Hour, Min, Sec}} = calendar:local_time(),
list:flatten(io_lib:format("~4..0w-~2..0w-~2..0w ~2..0w:~2..0w:~2..0w",
[Year, Month, Day, Hour, Min, Sec])).

Breaking It Down

  • -module(chat_utils). - Declares the module name (must match filename)
  • -export([format_message/2, current_timestamp/0]). - Makes functions public
  • format_message/2 - Function with 2 arguments (arity matters!)
  • current_timestamp/0 - Function with no arguments

Compile and Test

Terminal window
# In your project directory
rebar3 shell
# In the Erlang shell
1> c(chat_utils).
{ok,chat_utils}
2> chat_utils:format_message("Alice", "Hello everyone!").
"[2024-01-02 10:30:45] Alice: Hello everyone!"
3> chat_utils:current_timestamp().
"2024-01-02 10:30:45"

Erlang’s Core Data Types

Let’s explore the data types we’ll use throughout our chat server:

-module(data_demo).
-export([demonstrate/0]).
demonstrate() ->
% Numbers
Integer = 42,
Float = 3.14,
% Atoms (constants that represent themselves)
Status = ok,
Action = join_room,
% Strings (actually lists of characters)
Username = "Alice",
Message = "Hello World!",
% Lists (can contain any types)
Numbers = [1, 2, 3, 4, 5],
Mixed = [1, hello, "world", 3.14],
% Tuples (fixed-size containers)
Result = {ok, "Success"},
User = {user, "Alice", online},
% Binary strings (efficient for large text)
BinaryMsg = <<"Hello in binary">>,
% Print everything
io:format("Integer: ~p~n", [Integer]),
io:format("Status: ~p~n", [Status]),
io:format("Username: ~p~n", [Username]),
io:format("Numbers: ~p~n", [Numbers]),
io:format("User tuple: ~p~n", [User]),
io:format("Binary: ~p~n", [BinaryMsg]).

Key points:

  • Atoms are constants like ok, error, join_room
  • Strings are lists of characters: "Hello" = [72, 101, 108, 108, 111]
  • Tuples group related data: {ok, Value} or {error, Reason}
  • Binaries are efficient for large text data

Simple Functions with Basic Logic

Let’s create some simple functions that use basic conditionals:

-module(simple_math).
-export([max_of_two/2, is_even/1, grade_letter/1]).
% Find maximum of two numbers
max_of_two(A, B) when A >= B ->
A;
max_of_two(A, B) when A < B ->
B.
% Check if number is even
is_even(N) when N rem 2 =:= 0 ->
true;
is_even(_) ->
false.
% Convert numeric grade to letter
grade_letter(Score) when Score >= 90 ->
"A";
grade_letter(Score) when Score >= 80 ->
"B";
grade_letter(Score) when Score >= 70 ->
"C";
grade_letter(Score) when Score >= 60 ->
"D";
grade_letter(_) ->
"F".

What’s happening:

  • Multiple function clauses with different conditions
  • Guards (when) test conditions
  • Functions return different values based on input

Exercises - Type and Test!

Exercise 1: Basic Calculator

Create a module called calculator.erl:

-module(calculator).
-export([add/2, subtract/2, multiply/2, divide/2]).
add(A, B) when is_number(A), is_number(B) ->
A + B;
add(_, _) ->
{error, invalid_input}.
subtract(A, B) when is_number(A), is_number(B) ->
A - B;
subtract(_, _) ->
{error, invalid_input}.
multiply(A, B) when is_number(A), is_number(B) ->
A * B;
multiply(_, _) ->
{error, invalid_input}.
divide(A, B) when is_number(A), is_number(B), B =/= 0 ->
A / B;
divide(_, 0) ->
{error, division_by_zero};
divide(_, _) ->
{error, invalid_input}.

Exercise 2: String Utilities

Create a simple string utility module:

-module(string_utils).
-export([is_empty/1, length_category/1]).
is_empty(<<>>) ->
true;
is_empty(_) ->
false.
length_category(Text) when is_binary(Text), byte_size(Text) < 10 ->
short;
length_category(Text) when is_binary(Text), byte_size(Text) < 50 ->
medium;
length_category(Text) when is_binary(Text) ->
long;
length_category(_) ->
invalid.

Key Takeaways

  1. Modules organize code - one module per file, name must match
  2. Export makes functions public - function_name/arity
  3. Pattern matching is used everywhere - function heads, case expressions
  4. Guards add extra conditions with when
  5. Tuples like {ok, Value} and {error, Reason} are standard return patterns
  6. Binary strings (<<"text">>) are efficient for text processing
  7. Atoms are constants that represent themselves

What’s Next?

In Lesson 2, we’ll explore Erlang’s most powerful feature - pattern matching! You’ll learn how to:

  • Match and extract values from lists and tuples
  • Use pattern matching in function heads
  • Build elegant parsers and data processors
  • Handle complex data structures

The foundation you’ve built here will make pattern matching much easier to understand!


Test Your Understanding

Before moving on, make sure you can:

  1. Create a module with exported functions
  2. Use different data types (atoms, binaries, tuples, lists)
  3. Write guards with when
  4. Create functions that return different values based on input
  5. Return {ok, Value} or {error, Reason} patterns

If any of these feel unclear, review the examples and try the exercises again. Understanding modules and functions is essential for everything we’ll build next!


🧘

Koans - Test Your Understanding

Fill in the blanks and press Enter or click Run to test your knowledge!

What is 5 + 3?

Erlang Shell
1> Result = 5 + 3,
1> Result = ___.

What is X - Y?

Erlang Shell
1> X = 10,
1> Y = 4,
1> X - Y = ___.

What value does MyVar hold?

Erlang Shell
1> MyVar = 42,
1> MyVar = ___.

Fill in the missing variable name:

Erlang Shell
1> A = 5,
1> B = 3,
1> ___ = A + B,
1> Sum = 8.

After pattern matching, what value does A hold?

Erlang Shell
1> MyList = [1, 2, 3],
1> [A, B, C] = MyList,
1> A = ___.

What list makes Second = 20?

Erlang Shell
1> Numbers = ___,
1> [First, Second, Third] = Numbers,
1> Second = 20.

What value does Status hold?

Erlang Shell
1> Result = {ok, 42},
1> {Status, Value} = Result,
1> Status = ___.

What function gives the absolute value?

Erlang Shell
1> Number = -5,
1> Result = ___(Number),
1> Result = 5.
💡 Hint
The abs function returns the absolute value of a number

This is part of the “Learn Erlang Step-By-Step” tutorial series. Each lesson builds on the previous ones, so make sure you complete the exercises before moving forward.

Finished this lesson?

Mark it as complete to track your progress

This open source tutorial is brought to you by Pennypack Software - we build reliable software systems.

Found an issue? Edit this page on GitHub or open an issue