Variables and Types in OCaml
Learn about let bindings, type inference, algebraic data types, and the option type in OCaml with practical Docker-ready examples
In most languages, variables are mutable containers that hold values. OCaml takes a different approach. With its roots in the ML family of functional languages, OCaml uses let bindings — names bound to values that are immutable by default. Combined with a powerful type inference engine, this means you rarely write type annotations while still getting full compile-time type safety.
OCaml’s type system is static, strong, and inferred. The compiler determines types automatically through Hindley-Milner type inference, catching type errors before your program ever runs. There are no implicit type conversions — if you want to add an integer to a float, you must convert explicitly. This strictness might feel rigid at first, but it eliminates entire classes of bugs.
In this tutorial you’ll learn how let bindings work, explore OCaml’s built-in types, see how the type system enforces correctness, and discover algebraic data types and the option type — OCaml’s elegant replacement for null.
Let Bindings and Basic Types
OCaml’s primitive types include int, float, string, char, and bool. You bind names to values with let, and the compiler infers the type from the value.
Create a file named variables.ml:
| |
This single file demonstrates the full range of OCaml’s type system, from basic bindings through algebraic data types.
Understanding the Code
Immutable Let Bindings
The let keyword creates a binding between a name and a value. These bindings are immutable — once x is bound to 42, it cannot be changed. You can create a new binding with the same name (shadowing), but the original value is unchanged:
| |
Strict Type Separation
OCaml uses different operators for integers and floats: + for int, +. for float. There is no automatic promotion. Conversion functions like float_of_int and int_of_float make conversions explicit. This prevents subtle precision bugs that plague languages with implicit coercion.
Tuples and Records
Tuples group values of different types without defining a named type. Records are like tuples with named fields — they require a type definition but make code self-documenting. The functional update syntax ({ record with field = value }) creates a new record with one or more fields changed, leaving the original untouched.
The Option Type
Instead of null or nil, OCaml uses the option type: a value is either Some v or None. The compiler forces you to handle both cases through pattern matching, making null pointer exceptions impossible.
Variant Types
Variant types (also called algebraic data types or sum types) let you define a type as one of several alternatives. Each variant can carry different data. The function keyword creates an anonymous function that immediately pattern matches on its argument — a concise way to write functions that dispatch on type variants.
Running with Docker
| |
Expected Output
x = 42
pi = 3.141590
name = OCaml
letter = A
is_functional = true
OCaml first appeared in 1996
float_of_int 5 +. 2.5 = 7.5
int: 10 + 20 = 30
float: 1.5 +. 2.5 = 4
point = (3, 4)
person = (Ada, 36, true)
red = { r=255; g=0; b=0 }
purple = { r=255; g=0; b=128 }
First even: 4
No even number found
Areas:
Circle: 78.54
Rectangle: 24.00
Triangle: 6.00
counter = 2
Key Concepts
- Let bindings are immutable by default — names are bound to values, not assigned to mutable containers. Use
reffor the rare cases where you need mutation. - Type inference handles almost everything — the compiler deduces types from usage, so explicit annotations are optional and typically used only for documentation or disambiguation.
- No implicit type coercion — integer and float arithmetic use separate operators (
+vs+.), and conversions likefloat_of_intmust be explicit. This prevents subtle precision bugs. - Tuples group heterogeneous values without needing a type definition, while records add named fields for clarity and require a type declaration.
- The
optiontype replaces null — values areSome vorNone, and pattern matching forces you to handle both cases at compile time. - Variant types (algebraic data types) let you define types as one of several alternatives, each carrying different data. Pattern matching with exhaustiveness checking ensures you handle every case.
- Mutable references (
ref,!,:=) are available when needed, but idiomatic OCaml favors immutable bindings and functional updates.
Comments
Loading comments...
Leave a Comment