In 30 seconds
Richard Eisenberg demonstrates OCaml’s built-in coercion operator using polymorphic variants, recursive types and lists. He then uses a private integer abbreviation to show how a module can control construction while allowing values to be viewed as integers.
- 1Variant subtypingEvery value of a narrower polymorphic variant can be a value of a compatible wider variant.
- 2No runtime conversionA list coercion is checked by the type system and does not walk or reallocate the list at runtime.
- 3Private representationsA private integer abbreviation lets a module enforce an invariant at construction while allowing coercion back to int.
Takeaways
- OCaml can coerce between types when its subtype relation permits it.
- Polymorphic variants with compatible tags and payloads provide a subtyping example.
- Coercions can work through recursive types and lists without runtime traversal or allocation.
- A private abbreviation can expose a representation while restricting construction and preserving an invariant.
Read the edited text by chapter
Coercion and polymorphic variants
Eisenberg introduces the built-in coercion syntax with a narrow and a wider variant. He demonstrates why payloads must be compatible.
I want to explore OCaml’s subtyping relation and coercion operator. This is a feature of ordinary OCaml, rather than a Jane Street-specific extension, and it has been available for some time.
Some OCaml types have values that form a subset of another type’s values. Consider polymorphic variants: a type foo containing only the tag `Foo and a type foo_bar containing `Foo or `Bar. Every foo value is also a valid foo_bar value.
The expression `(x : foo :> foo_bar)` asks the compiler to check whether every value of the source type is valid for the target. If the check succeeds, the expression has the target type. When the source type is already known, it can be omitted; I will use the explicit form to make the examples clear.

This is a built-in coercion operator, not a user-defined conversion. It only works when the actual definitions satisfy the subtype relationship. If `Foo carries an integer in the source but not in the target, the coercion fails; adding the corresponding integer payload to the target makes the example work again.
In this example the compiler’s error mentions the missing `Bar tag, which is true but is not the reason for failure. The incompatible integer payload is the important issue. The relationship also works recursively when the payloads themselves use compatible recursive types.
Recursive types and lists
The subtype relationship can extend through recursive definitions and lists. The coercion is a type check with no runtime conversion.
The coercion can go deeper: a foo list can be viewed as a foo_bar list. Unlike a conventional conversion that traverses and rebuilds a list, this operator is guaranteed to have no runtime effect. The compiler checks and changes the type, but the list is not reallocated or walked.

Private type abbreviations
A private integer abbreviation supports a construction invariant. Coercion exposes the representation in one direction without permitting arbitrary integers to become the private type.
Another application is a private type abbreviation. A module can declare `type t = private int` while implementing t as an integer. It can provide a constructor that accepts only positive values, giving the module control over an invariant while exposing the underlying representation.
The private type and int are distinct: using a t directly where an int is expected, or an int where a t is expected, does not type-check. But the coercion operator allows a t to be viewed as an int. It works under lists as well, so a list of t can be coerced to an int list without runtime work.

The reverse coercion is rejected. Allowing an arbitrary int to become t would bypass the positive-value invariant. The module controls construction of t values, while callers can inspect them through the exposed integer representation.
The same operator also works with object types. In the next video I will explore how coercion interacts with variance annotations on new data types.
More from this channel
1:34:29EN
1:34:36EN
15:48EN
1:48:21EN
1:20:57EN
55:03EN