Operators in OCaml
Learn arithmetic, comparison, logical, and functional operators in OCaml - including the float-specific operators, pipe, composition, and reference operators unique to ML-family languages
Operators are the building blocks of expressions. In OCaml they have characteristics that surprise newcomers from C-family languages: arithmetic operators are not overloaded between integers and floats, and several functional operators like |> and @@ shape how idiomatic OCaml is written.
Because OCaml is a statically-typed language with type inference, the compiler uses operators as strong type hints. Writing 1 + 2.0 is a compile-time error — you must use +. for floats. This rigidity is intentional: it eliminates a whole class of numeric bugs and keeps type inference predictable.
In this tutorial you will learn the full set of arithmetic, comparison, logical, string, functional, and reference operators that OCaml provides, along with how operator precedence works.
Arithmetic Operators
OCaml separates integer and floating-point arithmetic into two distinct sets of operators. The “dotted” forms (+., -., *., /., **) work on floats; the plain forms work on integers.
Create a file named operators.ml:
| |
Why two sets of arithmetic operators?
In a language with type inference, overloading + for both int and float would force the compiler to either pick a default (surprising) or demand annotations everywhere (verbose). By giving floats their own operators, OCaml keeps inference predictable and makes the type of every numeric expression visually obvious. The ** operator is an exception — it only exists for floats, since integer exponentiation is rarely what you want.
Structural vs Physical Equality
OCaml has two equality operators with very different meanings:
=— structural equality (compares contents, like Java’s.equals)==— physical equality (compares references, like Java’s==)
For everyday code you almost always want =. The same applies to <> (structural) versus != (physical).
The Pipe Operator |>
The pipe operator is one of OCaml’s most beloved features. It flips function application so data flows left-to-right, much like Unix pipes:
| |
Both expressions produce 3, but the piped version reads in the order the operations happen.
Running with Docker
| |
If you prefer compiled output for speed:
| |
Expected Output
=== Integer Arithmetic ===
a = 17, b = 5
a + b = 22
a - b = 12
a * b = 85
a / b = 3 (integer division truncates)
a mod b = 2
-a = -17 (unary negation)
=== Float Arithmetic ===
x +. y = 22.00
x -. y = 12.00
x *. y = 85.00
x /. y = 3.40
x ** y = 1419857.00 (power, float only)
sqrt 16.0 = 4.00
=== Comparison Operators ===
5 = 5 -> true (structural equality)
5 <> 6 -> true (structural inequality)
5 < 6 -> true
5 > 6 -> false
5 <= 5 -> true
5 >= 6 -> false
=== Logical Operators ===
true && false -> false
true || false -> true
not true -> false
(2 > 1) && (3 < 4) -> true
=== String Concatenation ===
Hello, OCaml!
=== Functional Operators ===
5 |> double |> add_one = 11
add_one @@ double @@ 5 = 11
=== Reference Operators ===
counter after +10, +5 = 15
=== Precedence ===
2 + 3 * 4 = 14 (* binds tighter than +)
(2 + 3) * 4 = 20 (parentheses override)
10 - 4 - 2 = 4 (- is left-associative)
Defining Your Own Operators
OCaml lets you define infix operators. An operator’s first character determines its precedence — for example, an operator starting with * binds tighter than one starting with +.
Create a file named custom_ops.ml:
| |
Run it:
| |
Expected output:
(double >> add_one) 5 = 11
(double >> add_one) 10 = 21
This >> operator does forward composition: (f >> g) x evaluates to g (f x). It is a common idiom in functional OCaml code, though it isn’t part of the standard library — you define it where you need it.
Key Concepts
- Integer and float arithmetic are separate: use
+ - * /for integers and+. -. *. /.for floats. Mixing them is a type error. **is the float power operator — there’s no built-in integer exponentiation.=is structural equality,==is physical (reference) equality. Use=and<>in almost all cases.&&and||short-circuit like in most languages;notis the boolean negation function.^concatenates strings; it is not addition. Lists use@for concatenation and::to prepend.|>(pipe) and@@(application) rewrite function application for readability.x |> fequalsf x;f @@ xalso equalsf xbut is right-associative with low precedence so it removes parentheses.- References use
ref,:=, and!: create withref value, assign with:=, dereference with the prefix!. - You can define your own infix operators; their first character determines their precedence class.
Comments
Loading comments...
Leave a Comment