Standard ML
Where ML came from
- 1973: Robin Milner at Edinburgh creates ML as the “meta language”
for the LCF theorem prover
- The goal: write proof tactics that can’t forge theorems, so the type
system guards soundness
- That need produced Hindley-Milner type inference and exceptions as a
control tool
Standardization and descendants
- 1983-1990: ML splinters into dialects, then is unified as Standard
ML, with The Definition of Standard ML (1990, revised
1997)
- Milner wins the Turing Award in 1991
- Implementations: SML/NJ, MLton, Poly/ML
- Heirs: OCaml, F#, and large parts of Haskell, Rust and Scala
- Still used in theorem provers (HOL, Isabelle) and verified compilers
(CakeML)
Why start with values and types
- Everything in SML is an expression that produces a value, and every
value has a type
- The compiler infers types, so you rarely write them out
- Use cases: quick calculations, and reading the inferred type to
check your own understanding of the code
Values and types
Why functions
- Functions are ordinary values: you can name them, pass them and
return them
fn x => ... creates a function and
fun f x = ... is shorthand for naming one
- Use cases: small reusable transformations, and composing behavior
with
o
Functions
Why recursion and patterns
- There are no loops: repetition is recursion
- A function can be defined by clauses that match the shape of its
argument
- Use cases: anything inductive, such as factorials, tree walks and
parsers
Recursion
Why lists and higher-order functions
- Immutable singly linked lists are the default collection
map, filter and foldl replace
most hand-written loops
- Use cases: data pipelines, aggregating records, and transforming
input before processing it
Lists
Why datatypes
- A datatype lists every shape a value can take, and pattern matching
handles each one
- The compiler warns when a case is missing
- Use cases: ASTs and interpreters, state machines, and protocol
messages
Datatypes and pattern matching
Why polymorphism
- A function that doesn’t inspect its argument gets a general type
automatically
'a is a type variable: one definition works for every
type
- Use cases: generic containers, utilities like
swap and
compose, and library code
Polymorphism and inference
Why options
- There is no null: a value that may be absent has type
'a option
- The type forces callers to handle
NONE
- Use cases: lookups, searching, parsing, and anything partial
Options instead of null
Why exceptions
- Options model expected absence, while exceptions model failures that
cut across layers
raise throws, and handle pattern matches
on what was thrown
- Use cases: invalid input, resource errors, and early exit from deep
recursion
Exceptions
Why modules
- Structures group code, signatures describe an interface, and
:> hides the representation
- It is the language’s main tool for abstraction, in place of
classes
- Use cases: abstract data types, swappable implementations, and large
codebases with enforced boundaries
Modules
Try it yourself