Variables and Types in Gleam
Learn about let bindings, basic types, custom types, and type inference in Gleam with practical Docker-ready examples
Gleam is a statically typed functional language where every binding is immutable and the compiler infers types automatically. Unlike many languages, Gleam draws a hard line between integers and floats — they have entirely separate operators and cannot be mixed without explicit conversion. This strict separation, combined with custom algebraic data types and exhaustive pattern matching, means the compiler catches a wide range of bugs before your code ever runs.
As a functional language, Gleam has no mutable variables. Instead, you create immutable let bindings. If you need a new value, you create a new binding — you can even reuse the same name through shadowing, which creates a fresh binding that replaces the previous one in scope.
In this tutorial, you’ll explore Gleam’s basic types, see how Int and Float stay strictly separate, work with type annotations and conversions, and build your own custom types with variants and generics.
Let Bindings and Basic Types
Gleam has five basic types: String, Int, Float, Bool, and Nil. Every value is created with a let binding, and the compiler infers the type from the value on the right-hand side.
Create a file named variables.gleam:
| |
A few things stand out here. Gleam has no toString method on values — you use module functions like int.to_string and float.to_string for explicit conversion to strings. The <> operator concatenates strings. And since there’s no built-in bool.to_string, we wrote a small helper using pattern matching — a natural Gleam pattern.
The strict separation between Int and Float is intentional. You cannot write 1 + 2.5 — the compiler will reject it. Integer operators (+, -, *, /) and float operators (+., -., *., /.) are distinct, forcing you to be explicit about which numeric type you’re working with. This prevents subtle precision bugs that plague languages with implicit numeric coercion.
Custom Types, Generics, and Collections
Gleam’s custom types are algebraic data types — they can have multiple variants, each carrying different data. Combined with exhaustive pattern matching, the compiler ensures you handle every possible case. Types can also be generic, accepting type parameters that make them reusable across different data.
Create a file named variables_custom.gleam:
| |
Custom types are central to Gleam programming. The Color type shows how variants can range from simple labels (Red, Green, Blue) to variants carrying data (Custom with RGB values). The Point type demonstrates single-variant types that act like records — you get field access with dot notation and update syntax with .. to create modified copies.
The Pair type introduces generics: Pair(a, b) works with any types, and the compiler infers the concrete types from how you use it. Tuples serve a similar purpose for quick, anonymous groupings without defining a named type.
Lists in Gleam are singly-linked and immutable. Prepending with [0, ..numbers] is efficient because it reuses the existing list. Pattern matching on lists with [head, ..rest] is the idiomatic way to destructure them, and the recursive list_length function shows how functional iteration replaces loops.
Running with Docker
| |
Expected Output
Output from variables.gleam:
=== Basic Types ===
String: Gleam
Int: 2016
Float: 1.14
Bool: True
=== Type Annotations ===
Gleam v1
Rating: 9.5
=== Separate Numeric Types ===
Int: 10 + 25 = 35
Float: 1.5 +. 2.5 = 4.0
=== Type Conversions ===
Int to Float: 42 -> 42.0
Float rounded: 3.75 -> 4
Float truncated: 3.75 -> 3
=== Shadowing ===
count = 1
count (rebound) = 2
Output from variables_custom.gleam:
=== Custom Types ===
Color: Blue
Custom: RGB(255, 128, 0)
=== Record-Style Types ===
Origin: (0.0, 0.0)
Point: (3.0, 4.0)
Moved: (3.0, 10.0)
=== Generic Types ===
Pair: (age, 30)
=== Tuples ===
x=10, y=20
=== Lists ===
First element: 0
List length: 6
Key Concepts
- All bindings are immutable — there are no mutable variables in Gleam. Use shadowing (rebinding the same name) when you need an updated value.
- Type inference handles most cases — the compiler deduces types from values, so annotations are optional but useful as documentation.
- Int and Float are strictly separate — they use different operators (
+vs+.) and require explicit conversion functions likeint.to_floatandfloat.round. - Custom types are algebraic data types — they can have multiple variants, each carrying different data, and the compiler forces exhaustive pattern matching over all variants.
- Single-variant types work like records — they support dot notation for field access and
..syntax for creating modified copies. - Generic types use type parameters —
Pair(a, b)works with any types, inferred automatically from usage. - Lists are singly-linked and immutable — prepending is efficient, and pattern matching with
[head, ..rest]is the standard way to process them. - No null, no exceptions — Gleam uses
Optionfor missing values andResultfor operations that can fail, keeping the type system honest.
Running Today
All examples can be run using Docker:
docker pull ghcr.io/gleam-lang/gleam:v1.14.0-erlang-alpine
Comments
Loading comments...
Leave a Comment