Beginner

Operators in Erlang

Explore arithmetic, comparison, boolean, bitwise, and list operators in Erlang, plus pattern matching with the = operator

Operators in Erlang are expressions that combine values to produce new values. Because Erlang is a functional language with immutable data, operators never mutate their arguments — they always return fresh terms. That seemingly small detail shapes how you build programs: there is no ++= shortcut, no i++, and no in-place updates.

Erlang’s operator set covers the usual arithmetic, comparison, and boolean territory, but it also has a few personality traits worth knowing about. There are two equality operators (== and =:=), short-circuit and non-short-circuit boolean forms (andalso vs and), separate operators for integer and float division (div vs /), and the famous = which is not assignment but pattern matching. The list operators ++ and -- also stand in for what other languages might call string concatenation.

This tutorial walks through every operator group with runnable examples, then closes with a brief note on operator precedence so you know when to reach for parentheses.

Arithmetic Operators

Erlang separates integer and float division. The / operator always returns a float, while div and rem operate only on integers.

OperatorMeaning
+ - *Add, subtract, multiply
/Float division (always returns a float)
divInteger division (truncates)
remInteger remainder
-XUnary negation

Comparison and Equality

Erlang has two equality operators that catch beginners off guard:

  • == — arithmetic equality (5 == 5.0 is true)
  • =:= — exact equality (5 =:= 5.0 is false because types differ)

The /= and =/= operators are their negations. Erlang can also compare terms of different types using a documented ordering: number < atom < reference < fun < port < pid < tuple < map < list < binary.

Boolean Operators

Erlang offers two flavors of boolean operators:

  • and, or, xor, not — evaluate both sides (no short-circuit)
  • andalso, orelseshort-circuit versions, like && and || in other languages

Use andalso/orelse in guards and conditionals where you want the right-hand side skipped if the left already decides the answer.

List and String Operators

Strings in Erlang are just lists of integers, so list concatenation doubles as string concatenation:

  • ++ — concatenate two lists
  • -- — list subtraction (remove the first occurrence of each element on the right)

Bitwise Operators

For bit-level work Erlang uses worded operators rather than punctuation: band, bor, bxor, bnot, bsl (left shift), bsr (right shift).

The Match Operator (=)

The single equals sign in Erlang is not assignment — it is pattern matching. The left side is a pattern; if the right side matches, any unbound variables in the pattern get bound. You cannot rebind a variable that is already bound to a different value within the same scope.

A Comprehensive Example

Create a file named operators.erl:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#!/usr/bin/env escript
main(_) ->
    %% Arithmetic
    io:format("=== Arithmetic ===~n"),
    io:format("7 + 3   = ~p~n", [7 + 3]),
    io:format("7 - 3   = ~p~n", [7 - 3]),
    io:format("7 * 3   = ~p~n", [7 * 3]),
    io:format("10 / 4  = ~p~n", [10 / 4]),
    io:format("10 div 4 = ~p~n", [10 div 4]),
    io:format("10 rem 4 = ~p~n", [10 rem 4]),
    io:format("-7      = ~p~n", [-7]),

    %% Comparison and equality
    io:format("~n=== Comparison ===~n"),
    io:format("5 == 5.0   -> ~p~n", [5 == 5.0]),
    io:format("5 =:= 5.0  -> ~p~n", [5 =:= 5.0]),
    io:format("5 /= 5.0   -> ~p~n", [5 /= 5.0]),
    io:format("5 =/= 5.0  -> ~p~n", [5 =/= 5.0]),
    io:format("3 < 10     -> ~p~n", [3 < 10]),
    io:format("3 >= 3     -> ~p~n", [3 >= 3]),
    io:format("atom < 1   -> ~p~n", [atom < 1]),

    %% Boolean operators
    io:format("~n=== Boolean ===~n"),
    io:format("true and false   -> ~p~n", [true and false]),
    io:format("true or false    -> ~p~n", [true or false]),
    io:format("true xor false   -> ~p~n", [true xor false]),
    io:format("not true         -> ~p~n", [not true]),
    io:format("(1 < 2) andalso (3 < 4) -> ~p~n", [(1 < 2) andalso (3 < 4)]),
    io:format("(1 > 2) orelse  (3 < 4) -> ~p~n", [(1 > 2) orelse (3 < 4)]),

    %% Bitwise operators
    io:format("~n=== Bitwise ===~n"),
    io:format("5 band 3 = ~p~n", [5 band 3]),
    io:format("5 bor 3  = ~p~n", [5 bor 3]),
    io:format("5 bxor 3 = ~p~n", [5 bxor 3]),
    io:format("1 bsl 4  = ~p~n", [1 bsl 4]),
    io:format("16 bsr 2 = ~p~n", [16 bsr 2]),
    io:format("bnot 0   = ~p~n", [bnot 0]),

    %% List and string operators
    io:format("~n=== List Operators ===~n"),
    L1 = [1, 2, 3],
    L2 = [4, 5, 6],
    io:format("[1,2,3] ++ [4,5,6]     = ~p~n", [L1 ++ L2]),
    io:format("[1,2,3,4,5] -- [2,4]   = ~p~n", [[1, 2, 3, 4, 5] -- [2, 4]]),
    Greeting = "Hello, " ++ "World!",
    io:format("\"Hello, \" ++ \"World!\" = ~s~n", [Greeting]),

    %% Pattern matching with =
    io:format("~n=== Pattern Matching (=) ===~n"),
    {X, Y, Z} = {10, 20, 30},
    io:format("{X,Y,Z} = {10,20,30}  -> X=~p Y=~p Z=~p~n", [X, Y, Z]),
    [Head | Tail] = [1, 2, 3, 4],
    io:format("[H|T] = [1,2,3,4]     -> H=~p T=~p~n", [Head, Tail]),

    %% Precedence demonstration
    io:format("~n=== Precedence ===~n"),
    io:format("2 + 3 * 4         = ~p~n", [2 + 3 * 4]),
    io:format("(2 + 3) * 4       = ~p~n", [(2 + 3) * 4]),
    io:format("1 + 2 == 3        -> ~p~n", [1 + 2 == 3]),
    io:format("not true and false -> ~p~n", [not true and false]).

Running with Docker

1
2
3
4
5
# Pull the official Erlang image
docker pull erlang:alpine

# Run the operators example
docker run --rm -v $(pwd):/app -w /app erlang:alpine escript operators.erl

Expected Output

=== Arithmetic ===
7 + 3   = 10
7 - 3   = 4
7 * 3   = 21
10 / 4  = 2.5
10 div 4 = 2
10 rem 4 = 2
-7      = -7

=== Comparison ===
5 == 5.0   -> true
5 =:= 5.0  -> false
5 /= 5.0   -> false
5 =/= 5.0  -> true
3 < 10     -> true
3 >= 3     -> true
atom < 1   -> false

=== Boolean ===
true and false   -> false
true or false    -> true
true xor false   -> true
not true         -> false
(1 < 2) andalso (3 < 4) -> true
(1 > 2) orelse  (3 < 4) -> true

=== Bitwise ===
5 band 3 = 1
5 bor 3  = 7
5 bxor 3 = 6
1 bsl 4  = 16
16 bsr 2 = 4
bnot 0   = -1

=== List Operators ===
[1,2,3] ++ [4,5,6]     = [1,2,3,4,5,6]
[1,2,3,4,5] -- [2,4]   = [1,3,5]
"Hello, " ++ "World!" = Hello, World!

=== Pattern Matching (=) ===
{X,Y,Z} = {10,20,30}  -> X=10 Y=20 Z=30
[H|T] = [1,2,3,4]     -> H=1 T=[2,3,4]

=== Precedence ===
2 + 3 * 4         = 14
(2 + 3) * 4       = 20
1 + 2 == 3        -> true
not true and false -> false

A Note on Term Ordering

Comparison operators work across types using a fixed term ordering. The result of atom < 1 is false because atoms are greater than numbers in Erlang’s term order. The full ordering is:

number < atom < reference < fun < port < pid < tuple < map < list < binary

This is occasionally surprising but useful — it means you can sort any list of terms with lists:sort/1 and get a deterministic result.

Operator Precedence (Lowest to Highest)

Erlang’s precedence table, condensed:

  1. catch
  2. = ! (match and send)
  3. orelse
  4. andalso
  5. == /= =< < >= > =:= =/=
  6. ++ --
  7. + - bor bxor bsl bsr or xor
  8. * / div rem band and
  9. Unary + - bnot not

Notice that == has lower precedence than +, so 1 + 2 == 3 parses as (1 + 2) == 3 and yields true. Also note that not binds tighter than and, so not true and false is (not true) and false, which is false and false = false.

When in doubt, add parentheses — they cost nothing and make intent obvious.

Key Concepts

  • Two equality operators: == compares arithmetic value (5 == 5.0 is true), while =:= requires identical types (5 =:= 5.0 is false).
  • Two division operators: / returns a float always; div and rem are integer-only.
  • Short-circuit vs. eager booleans: prefer andalso and orelse in conditionals — and/or evaluate both operands and only accept booleans strictly.
  • Bitwise operators are worded: use band, bor, bxor, bnot, bsl, bsr — there is no &, |, or <<.
  • ++ and -- work on lists, and because strings are lists, the same operators concatenate strings.
  • = is pattern matching, not assignment: it binds variables on the left if they’re unbound, and asserts equality if they’re already bound — a mismatch raises an error.
  • Cross-type comparisons are legal thanks to Erlang’s total term ordering, useful for sorting heterogeneous lists.
  • Unary not has the highest precedence among boolean operators, so not A and B means (not A) and B.

Running Today

All examples can be run using Docker:

docker pull erlang:alpine
Last updated:

Comments

Loading comments...

Leave a Comment

2000 characters remaining