jonnifyfsharp

Dive C0.1.2

The ML family

A type system that works out the types, so a programmer almost never writes them.

In the early 1970s Robin Milner built ML as the language for writing proofs in a theorem prover. To keep the proofs honest he needed a type system — and the one he reached for, worked out with J. Roger Hindley, gives the compiler the job of inferring the types. Hindley–Milner inference is why F# code can stay free of type annotations and still be fully statically typed. This dive follows the family that carried it — Standard ML, and the Caml/OCaml line F# most resembles.

C0.1.2 · part 1

ML, born to keep proofs honest

Robin Milner created ML at Edinburgh in the early 1970s. The name is short for Meta Language: ML was the language for writing the tactics that drove LCF (Logic for Computable Functions), an interactive proof assistant. The type system carried a heavy load — it made theorem an abstract type whose values could be produced only by the rules of the logic, so the type checker itself guaranteed that anything of type theorem had actually been proved.

That setting shaped the language. ML was functional, it had algebraic datatypes and pattern matching to take them apart, and — the part that travelled furthest — it had a type system with full inference, so the programmer wrote tactics without spelling out types and the compiler filled them in.

created by
Robin Milner, at Edinburgh, early 1970s.
ML stands for
Meta Language — of the LCF proof assistant.
the trick
theorem was an abstract type; only valid proofs could build one.
it brought
datatypes, pattern matching, and full type inference.

Hindley–Milner: the compiler finds the type

The type system Milner reached for had also been found, independently, by the logician J. Roger Hindley (1969); Milner published his account in 1978, A Theory of Type Polymorphism in Programming, and the inference algorithm — Algorithm W — was set out by Luis Damas and Milner in 1982. Hence Hindley–Milner, and the algorithm is often called Damas–Milner.

It delivers three things at once: full type inference (a type is rarely written down), parametric polymorphism (a function over 'a works for every type), and soundness — Milner's slogan, well-typed programs do not go wrong. Select an expression below to see the most general type the algorithm assigns it.

Principal types · select an expression

let id x = x expression Hindley– Milner 'a -> 'a principal type
let id x = x
// inferred:  val id : 'a -> 'a
id · 'a -> 'a · x is used as-is, so its type is left free and generalised to every type

No type was written, yet each value has a precise, fully general type. Inference reads the most general type off the way each value is used — the convenience F# inherits whole.

The ML family tree below shows where the inference travelled. From Milner's ML, two lines branch: Standard ML, formally specified, and the French Caml/OCaml line from INRIA — the dialect F# most resembles.

The ML family tree

ML Milner · ~1973 Standard ML Definition · 1990 Caml INRIA · mid-1980s OCaml 1996 F# 2005
OCaml · the line F# most resembles · the dashed edge marks F#'s descent from OCaml's core
C0.1.2 · part 2

Two lines: Standard ML and OCaml

Standard ML is the formally defined member of the family. The Definition of Standard ML — by Milner, Tofte, and Harper, 1990, revised 1997 — specifies the language down to its operational semantics, a rare degree of rigour. SML gives the family its module system with functors, its datatypes, and pattern matching.

The other line is French. Caml grew at INRIA in the mid-1980s; it led through Caml Light to Objective Caml, or OCaml, released in 1996 (Xavier Leroy and colleagues), which added an object system and a fast native-code compiler. OCaml is the dialect F# most directly resembles in syntax and core semantics — the next dive is about how F# put that core on .NET.

Standard ML
The Definition of Standard ML — Milner, Tofte, Harper (1990; rev. 1997).
Caml → OCaml
INRIA's line; Caml (mid-1980s) → Caml Light → OCaml (1996).
OCaml added
objects and a fast native compiler, on the ML core.
nearest to F#
OCaml — F# starts from its syntax and semantics.

The ML family

Inference, parametric polymorphism, algebraic data, and pattern matching — refined across SML and OCaml.

F# today

F# carries the whole set: 'a -> 'a generics, discriminated unions, records, and match.

What F# inherits from this family is the typed core: inference, polymorphism, algebraic data, and pattern matching — the OCaml shape, ready to be carried onto a new runtime.

The thread into F#

The first dive made functions first-class. This one made them typed — without making a programmer write the types. Hindley–Milner inference, parametric polymorphism, datatypes, and pattern matching are the ML family's gift, and F# takes them whole through OCaml.

The last dive in this module, C0.1.3, is about the move that made F# its own language: OCaml's core retargeted to the .NET runtime, with full access to the platform's libraries.

References

Sources

Related in this course