Beginner

Operators in Gleam

Learn arithmetic, comparison, logical, and pipe operators in Gleam, including the separate operators for integers and floats

Operators are the building blocks of expressions. Gleam’s operator set reflects its functional, statically-typed nature: every operator has a single, well-defined type signature, and the compiler will refuse to compile expressions that mix incompatible types.

The most distinctive aspect of Gleam’s operators is the strict separation between integers and floats. Unlike most languages, you cannot use + to add two floats together. Instead, Gleam provides a parallel family of dot-suffixed operators (+., -., *., /.) specifically for Float values. This design eliminates an entire class of numeric precision bugs at compile time.

Beyond arithmetic, Gleam offers a string concatenation operator (<>), boolean logic (&&, ||, !), and the pipe operator (|>) that is central to idiomatic Gleam code. There are no compound assignment operators like += because Gleam values are immutable — you cannot mutate a binding in place.

Integer and Float Arithmetic

Gleam separates integer and float arithmetic to keep numeric semantics unambiguous. Mixing them is a compile error, not a silent coercion.

Create a file named operators.gleam:

 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
import gleam/io
import gleam/int
import gleam/float

pub fn main() {
  // Integer arithmetic uses unadorned operators
  let sum = 7 + 3
  let difference = 7 - 3
  let product = 7 * 3
  let quotient = 7 / 3
  let remainder = 7 % 3

  io.println("Integer arithmetic:")
  io.println("  7 + 3 = " <> int.to_string(sum))
  io.println("  7 - 3 = " <> int.to_string(difference))
  io.println("  7 * 3 = " <> int.to_string(product))
  io.println("  7 / 3 = " <> int.to_string(quotient))
  io.println("  7 % 3 = " <> int.to_string(remainder))

  // Float arithmetic uses dot-suffixed operators
  let fsum = 7.0 +. 3.0
  let fdiff = 7.0 -. 3.0
  let fprod = 7.0 *. 3.0
  let fquot = 7.0 /. 2.0

  io.println("")
  io.println("Float arithmetic:")
  io.println("  7.0 +. 3.0 = " <> float.to_string(fsum))
  io.println("  7.0 -. 3.0 = " <> float.to_string(fdiff))
  io.println("  7.0 *. 3.0 = " <> float.to_string(fprod))
  io.println("  7.0 /. 2.0 = " <> float.to_string(fquot))

  // Division by zero returns 0 in Gleam (no runtime exception)
  io.println("  10 / 0 = " <> int.to_string(10 / 0))
}

Note how int.to_string and float.to_string are required to combine numbers into a string — there is no automatic numeric-to-string coercion. Also notice that 10 / 0 evaluates to 0 rather than crashing; Gleam deliberately makes division total to avoid runtime errors.

Comparison and Boolean Logic

Equality works across any type with == and !=, but the ordering operators (<, >, <=, >=) are integer-specific. Floats have their own dotted ordering operators (<., >., <=., >=.).

Create a file named comparisons.gleam:

 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
import gleam/io
import gleam/bool

pub fn main() {
  // Equality works on any type, both sides must match
  let same_int = 5 == 5
  let diff_string = "yes" != "no"

  io.println("5 == 5      -> " <> bool.to_string(same_int))
  io.println("\"yes\" != \"no\" -> " <> bool.to_string(diff_string))

  // Integer ordering uses bare operators
  let int_lt = 3 < 7
  let int_ge = 10 >= 10
  io.println("3 < 7       -> " <> bool.to_string(int_lt))
  io.println("10 >= 10    -> " <> bool.to_string(int_ge))

  // Float ordering requires dotted operators
  let float_lt = 3.5 <. 7.2
  let float_ge = 9.9 >=. 9.9
  io.println("3.5 <. 7.2  -> " <> bool.to_string(float_lt))
  io.println("9.9 >=. 9.9 -> " <> bool.to_string(float_ge))

  // Boolean operators short-circuit
  let both = True && False
  let either = True || False
  let negated = !True

  io.println("True && False -> " <> bool.to_string(both))
  io.println("True || False -> " <> bool.to_string(either))
  io.println("!True         -> " <> bool.to_string(negated))

  // && and || require Bool on both sides — no truthy/falsy values
  let combined = 5 > 3 && 2 < 4
  io.println("5 > 3 && 2 < 4 -> " <> bool.to_string(combined))
}

Gleam has no concept of “truthy” or “falsy” values. The boolean operators only accept actual Bool values (True or False), so writing if some_string is impossible. You must always produce an explicit boolean.

String Concatenation and the Pipe Operator

The <> operator concatenates strings. The |> pipe operator is one of Gleam’s most idiomatic features — it threads a value through a series of function calls, passing the value as the first argument to each function.

Create a file named pipes.gleam:

 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
import gleam/io
import gleam/string
import gleam/int

pub fn main() {
  // String concatenation with <>
  let greeting = "Hello, " <> "Gleam" <> "!"
  io.println(greeting)

  // Without pipes — nested calls read inside-out
  let nested = string.uppercase(string.reverse("hello"))
  io.println("Nested:  " <> nested)

  // With pipes — the same logic reads top-to-bottom
  let piped =
    "hello"
    |> string.reverse
    |> string.uppercase
  io.println("Piped:   " <> piped)

  // Pipes pass the value as the first argument by default
  let count =
    [1, 2, 3, 4, 5]
    |> list_sum
    |> int.to_string

  io.println("Sum:     " <> count)

  // Operator precedence: arithmetic binds tighter than comparison,
  // which binds tighter than boolean logic
  let result = 2 + 3 * 4 > 10 && 1 < 2
  io.println("2 + 3 * 4 > 10 && 1 < 2 -> " <> case result {
    True -> "True"
    False -> "False"
  })
}

fn list_sum(numbers: List(Int)) -> Int {
  case numbers {
    [] -> 0
    [first, ..rest] -> first + list_sum(rest)
  }
}

Reading the piped version aloud — “start with hello, reverse it, then uppercase it” — matches how the computation actually proceeds, which is why Gleam programmers reach for |> so often.

Running with Docker

Gleam needs a project structure, so the Docker command creates one on the fly and copies your source file into it.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# Pull the official Gleam image
docker pull ghcr.io/gleam-lang/gleam:v1.14.0-erlang-alpine

# Run the arithmetic example
docker run --rm -v $(pwd):/work ghcr.io/gleam-lang/gleam:v1.14.0-erlang-alpine sh -c 'gleam new hello --skip-git > /dev/null 2>&1 && cp /work/operators.gleam hello/src/hello.gleam && cd hello && gleam run'

# Run the comparisons example
docker run --rm -v $(pwd):/work ghcr.io/gleam-lang/gleam:v1.14.0-erlang-alpine sh -c 'gleam new hello --skip-git > /dev/null 2>&1 && cp /work/comparisons.gleam hello/src/hello.gleam && cd hello && gleam run'

# Run the pipes example
docker run --rm -v $(pwd):/work ghcr.io/gleam-lang/gleam:v1.14.0-erlang-alpine sh -c 'gleam new hello --skip-git > /dev/null 2>&1 && cp /work/pipes.gleam hello/src/hello.gleam && cd hello && gleam run'

Expected Output

Running operators.gleam produces:

Integer arithmetic:
  7 + 3 = 10
  7 - 3 = 4
  7 * 3 = 21
  7 / 3 = 2
  7 % 3 = 1

Float arithmetic:
  7.0 +. 3.0 = 10.0
  7.0 -. 3.0 = 4.0
  7.0 *. 3.0 = 21.0
  7.0 /. 2.0 = 3.5
  10 / 0 = 0

Running comparisons.gleam produces:

5 == 5      -> True
"yes" != "no" -> True
3 < 7       -> True
10 >= 10    -> True
3.5 <. 7.2  -> True
9.9 >=. 9.9 -> True
True && False -> False
True || False -> True
!True         -> False
5 > 3 && 2 < 4 -> True

Running pipes.gleam produces:

Hello, Gleam!
Nested:  OLLEH
Piped:   OLLEH
Sum:     15
2 + 3 * 4 > 10 && 1 < 2 -> True

Key Concepts

  • Separate operators for Int and Float+ - * / % work only on integers; +. -. *. /. work only on floats. Mixing types is a compile error, not a runtime surprise.
  • Total arithmetic — Integer division by zero returns 0 rather than raising an exception, keeping all arithmetic functions total.
  • No truthy values&&, ||, and ! accept only Bool. There is no implicit conversion from numbers, strings, or lists to booleans.
  • String concatenation uses <> — The + operator does not concatenate strings; it is reserved for integer addition.
  • No compound assignment — Operators like += do not exist because bindings are immutable. Use let to bind a new value.
  • The pipe operator |> — Threads a value as the first argument through a chain of function calls, replacing nested calls with a top-to-bottom flow.
  • Ordering on floats is dotted too — Use <., >., <=., >=. to compare floats; the bare ordering operators are integer-only.
  • Equality is generic== and != work on any type, but both sides must have the same type or the compiler will reject the expression.

Running Today

All examples can be run using Docker:

docker pull ghcr.io/gleam-lang/gleam:v1.14.0-erlang-alpine
Last updated:

Comments

Loading comments...

Leave a Comment

2000 characters remaining