Skip to content

Instantly share code, notes, and snippets.

@Chubek
Created October 8, 2025 03:45
Show Gist options
  • Save Chubek/210ba1f14e585a371a4223cf10d3b30b to your computer and use it in GitHub Desktop.
Save Chubek/210ba1f14e585a371a4223cf10d3b30b to your computer and use it in GitHub Desktop.
<title>Dossier - typethm-dossier</title> <style> :root { /* Dracula color palette */ --bg-color: #1a1b26; --current-line: #24283b; --selection: #364a82; --foreground: #c0caf5; --comment: #565f89; --cyan: #8be9fd; --green: #50fa7b; --orange: #ffb86c; --pink: #ff79c6; --purple: #bd93f9; --red: #ff5555; --yellow: #f1fa8c;
  /* Semantic colors */
  --text-color: var(--foreground);
  --heading-color: var(--purple);
  --link-color: var(--cyan);
  --link-hover: var(--pink);
  --code-bg: var(--current-line);
  --code-border: #6272a4;
  --toc-bg: #21222c;
  --toc-border: var(--current-line);
  --blockquote-border: var(--purple);
  --table-border: var(--current-line);
}

* {
  box-sizing: border-box;
}

body {
  font-family: Georgia, 'Times New Roman', serif;
  line-height: 1.7;
  max-width: 1400px;
  margin: 0 auto;
  padding: 20px;
  background-color: var(--bg-color);
  color: var(--text-color);
  display: flex;
  gap: 2rem;
}

/* Table of Contents */
#TOC {
  position: sticky;
  top: 20px;
  min-width: 250px;
  max-width: 300px;
  height: fit-content;
  max-height: calc(100vh - 40px);
  overflow-y: auto;
  background-color: var(--toc-bg);
  border: 1px solid var(--toc-border);
  border-radius: 8px;
  padding: 1.5rem;
  font-size: 0.9rem;
}

#TOC ul {
  list-style: none;
  padding-left: 0;
  margin: 0;
}

#TOC > ul > li {
  margin-bottom: 0.5rem;
}

#TOC ul ul {
  padding-left: 1rem;
  margin-top: 0.3rem;
}

#TOC a {
  color: var(--link-color);
  text-decoration: none;
  display: block;
  padding: 0.2rem 0;
  transition: all 0.2s;
}

#TOC a:hover {
  color: var(--link-hover);
  transform: translateX(4px);
}

/* Main content */
main {
  flex: 1;
  min-width: 0;
  max-width: 800px;
}

h1, h2, h3, h4, h5, h6 {
  color: var(--heading-color);
  margin-top: 2rem;
  margin-bottom: 1rem;
  line-height: 1.3;
  font-weight: 600;
}

h1 { 
  font-size: 2.5rem; 
  border-bottom: 2px solid var(--purple); 
  padding-bottom: 0.5rem;
  color: var(--pink);
}
h2 { 
  font-size: 2rem; 
  border-bottom: 1px solid var(--current-line); 
  padding-bottom: 0.3rem; 
}
h3 { font-size: 1.5rem; color: var(--cyan); }
h4 { font-size: 1.25rem; color: var(--green); }
h5 { font-size: 1.1rem; color: var(--orange); }
h6 { font-size: 1rem; color: var(--yellow); }

a {
  color: var(--link-color);
  text-decoration: none;
  transition: color 0.2s;
}

a:hover {
  color: var(--link-hover);
  text-decoration: underline;
}

/* Code blocks */
pre {
  background-color: var(--code-bg);
  border: 1px solid var(--code-border);
  border-radius: 6px;
  padding: 1rem;
  overflow-x: auto;
  font-size: 0.9rem;
}

code {
  font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
  background-color: var(--code-bg);
  padding: 0.2rem 0.4rem;
  border-radius: 3px;
  font-size: 0.9em;
  color: var(--pink);
}

pre code {
  background-color: transparent;
  padding: 0;
  color: var(--foreground);
}

/* Blockquotes */
blockquote {
  margin: 1.5rem 0;
  padding: 1rem;
  padding-left: 1.5rem;
  border-left: 4px solid var(--blockquote-border);
  background-color: rgba(189, 147, 249, 0.1);
  color: var(--foreground);
  font-style: italic;
  border-radius: 0 6px 6px 0;
}

blockquote p {
  margin: 0.5rem 0;
}

/* Tables */
table {
  border-collapse: collapse;
  width: 100%;
  margin: 1.5rem 0;
}

table th,
table td {
  border: 1px solid var(--table-border);
  padding: 0.75rem;
  text-align: left;
}

table th {
  background-color: var(--code-bg);
  color: var(--purple);
  font-weight: bold;
}

table tr:nth-child(even) {
  background-color: rgba(68, 71, 90, 0.3);
}

table tr:hover {
  background-color: rgba(68, 71, 90, 0.5);
}

/* Images */
img {
  max-width: 100%;
  height: auto;
  border-radius: 6px;
  border: 1px solid var(--current-line);
}

/* Lists */
ul, ol {
  margin: 1rem 0;
  padding-left: 2rem;
}

li {
  margin: 0.5rem 0;
}

li::marker {
  color: var(--purple);
}

/* Horizontal rule */
hr {
  border: none;
  border-top: 2px solid var(--current-line);
  margin: 2rem 0;
}

/* Inline emphasis */
strong {
  color: var(--orange);
  font-weight: bold;
}

em {
  color: var(--yellow);
  font-style: italic;
}

/* Selection */
::selection {
  background-color: var(--selection);
  color: var(--foreground);
}

/* Responsive design */
@media (max-width: 900px) {
  body {
    flex-direction: column;
  }

  #TOC {
    position: static;
    max-width: 100%;
    max-height: 300px;
    margin-bottom: 2rem;
  }

  main {
    max-width: 100%;
  }
}

/* Scrollbar styling */
::-webkit-scrollbar {
  width: 12px;
}

::-webkit-scrollbar-track {
  background: var(--bg-color);
}

::-webkit-scrollbar-thumb {
  background: var(--current-line);
  border-radius: 6px;
}

::-webkit-scrollbar-thumb:hover {
  background: var(--comment);
}

/* Title block */
#title-block-header {
  margin-bottom: 3rem;
  padding-bottom: 1rem;
  border-bottom: 2px solid var(--purple);
}

#title-block-header .title {
  margin-top: 0;
  color: var(--pink);
}

#title-block-header .subtitle {
  color: var(--purple);
  font-size: 1.3rem;
  margin: 0.5rem 0;
}

#title-block-header .author {
  color: var(--cyan);
  margin: 0.3rem 0;
}

#title-block-header .date {
  color: var(--comment);
  font-size: 0.9rem;
  margin: 0.3rem 0;
}
</style> <style> html { -webkit-text-size-adjust: 100%; } pre > code.sourceCode { white-space: pre; position: relative; } pre > code.sourceCode > span { display: inline-block; line-height: 1.25; } pre > code.sourceCode > span:empty { height: 1.2em; } .sourceCode { overflow: visible; } code.sourceCode > span { color: inherit; text-decoration: inherit; } div.sourceCode { margin: 1em 0; } pre.sourceCode { margin: 0; } @media screen { div.sourceCode { overflow: auto; } } @media print { pre > code.sourceCode { white-space: pre-wrap; } pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; } } pre.numberSource code { counter-reset: source-line 0; } pre.numberSource code > span { position: relative; left: -4em; counter-increment: source-line; } pre.numberSource code > span > a:first-child::before { content: counter(source-line); position: relative; left: -1em; text-align: right; vertical-align: baseline; border: none; display: inline-block; -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; padding: 0 4px; width: 4em; color: #aaaaaa; } pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; } div.sourceCode { background-color: #f8f8f8; } @media screen { pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; } } code span.al { color: #ef2929; } /* Alert */ code span.an { color: #8f5902; font-weight: bold; font-style: italic; } /* Annotation */ code span.at { color: #204a87; } /* Attribute */ code span.bn { color: #0000cf; } /* BaseN */ code span.cf { color: #204a87; font-weight: bold; } /* ControlFlow */ code span.ch { color: #4e9a06; } /* Char */ code span.cn { color: #8f5902; } /* Constant */ code span.co { color: #8f5902; font-style: italic; } /* Comment */ code span.cv { color: #8f5902; font-weight: bold; font-style: italic; } /* CommentVar */ code span.do { color: #8f5902; font-weight: bold; font-style: italic; } /* Documentation */ code span.dt { color: #204a87; } /* DataType */ code span.dv { color: #0000cf; } /* DecVal */ code span.er { color: #a40000; font-weight: bold; } /* Error */ code span.ex { } /* Extension */ code span.fl { color: #0000cf; } /* Float */ code span.fu { color: #204a87; font-weight: bold; } /* Function */ code span.im { } /* Import */ code span.in { color: #8f5902; font-weight: bold; font-style: italic; } /* Information */ code span.kw { color: #204a87; font-weight: bold; } /* Keyword */ code span.op { color: #ce5c00; font-weight: bold; } /* Operator */ code span.ot { color: #8f5902; } /* Other */ code span.pp { color: #8f5902; font-style: italic; } /* Preprocessor */ code span.sc { color: #ce5c00; font-weight: bold; } /* SpecialChar */ code span.ss { color: #4e9a06; } /* SpecialString */ code span.st { color: #4e9a06; } /* String */ code span.va { color: #000000; } /* Variable */ code span.vs { color: #4e9a06; } /* VerbatimString */ code span.wa { color: #8f5902; font-weight: bold; font-style: italic; } /* Warning */ </style>

Dossier - typethm-dossier

<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/katex.min.js" integrity_no="sha384-XjKyOOlGwcjNTAIQHIpgOno0Hl1YQqzUOEleOLALmuqehneUG+vnGctmUb0ZY0l8" crossorigin="anonymous"></script> <script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/contrib/auto-render.min.js" integrity_no="sha384-+VBxd3r6XgURycqtZ117nYw44OOcIax56Z4dCRWbxyPt0Koah1uHoK0o4+/RRE05" crossorigin="anonymous"></script> <script> document.addEventListener("DOMContentLoaded", function() { renderMathInElement(document.body, { delimiters: [ {left: "", right: "", display: true}, {left: "\\[", right: "\\]", display: true}, {left: "\\(", right: "\\)", display: true} ], throwOnError: false }); }); </script>

Chapter 1 Introduction to Type Theory

In this chapter we introduce the fundamental ideas of type theory. We begin with the informal notion of a “type,” survey its historical roots and modern motivations, then present the basic judgmental framework (terms, contexts, typing rules) common to virtually every typed formalism. Finally, we fix notational and metatheoretic conventions that will guide the rest of the book.

  1. 1 What Is a “Type”? History and Motivation

1.1.1 Informal notion
A “type” is, at heart, a classification of computational or mathematical objects that constrains how they may be combined. In programming, types prevent meaningless operations (e.g. adding a Boolean to an integer), document programmer intent, and enable compiler optimizations. In logic, types correspond to propositions—proof‐terms inhabit types. In mathematics, types can model sets, categories, or algebraic structures.

1.1.2 Early origins
• Bertrand Russell’s Theory of Types (1908) sought to avoid paradoxes in set theory by stratifying sets into “types.”
• Alonzo Church (1940s) introduced the simply typed λ‐calculus, adding a layer of types to the untyped λ‐calculus.
• Haskell Curry and Robert Feys developed Combinatory Logic, which implicitly carries type discipline.
• In the 1970s, Robin Milner’s Hindley–Milner system formalized polymorphic type inference, underpinning ML and Haskell.

1.1.3 Modern motivations
1. Safety: Types rule out whole classes of runtime errors (ill‐formed memory accesses, mismatched data).
2. Abstraction: Interfaces expressed via types decouple modules and allow separate compilation.
3. Documentation: A type signature is a concise specification.
4. Optimization: Knowing types lets compilers generate more efficient code.
5. Verification: Dependent types allow expressing and machine‐checking rich correctness properties.

  1. 2 Basic Concepts: Terms, Judgments, Typing Rules

1.2.1 Syntax of Terms and Types
We start with the simply typed λ‐calculus (STLC) as our running example. Let metavariables
– x,y,zx,y,z range over term variables
– τ,σ\tau,\sigma range over types
– M,NM,N range over terms

Types τ\tau are given by the grammar
τ::=𝐁𝐨𝐨𝐥∣𝐍𝐚𝐭∣τ1→τ2 \tau ::= \mathbf{Bool} \mid \mathbf{Nat} \mid \tau_1 \to \tau_2
Terms MM by
M::=x∣λx:τ.M∣M1M2∣𝐭𝐫𝐮𝐞∣𝐟𝐚𝐥𝐬𝐞∣0∣succ(M) M ::= x \mid \lambda x:\tau.\;M \mid M_1\;M_2 \mid \mathbf{true}\mid \mathbf{false} \mid 0\mid\mathrm{succ}(M)

1.2.2 Typing contexts and judgments
A context Γ\Gamma is a finite mapping from term variables to types, e.g.
Γ={x:τ1,y:τ2,…}. \Gamma = \{\,x:\tau_1,\;y:\tau_2,\;\dots\}.
A typing judgment has the form
Γ⊢M:τ \Gamma\;\vdash\;M:\tau
and asserts “under assumptions in Γ\Gamma, term MM has type τ\tau.”

1.2.3 Inference rules
We present several standard rules in natural deduction style.

(Var)
$$ \infer[\textsc{Var}] {\Gamma\vdash x:\tau} {x:\tau\in\Gamma} $$

(Abs)
$$ \infer[\textsc{Abs}] {\Gamma\vdash \lambda x:\tau_1.\;M:\;\tau_1\to\tau_2} {\Gamma,x:\tau_1\;\vdash M:\tau_2} $$

(App)
$$ \infer[\textsc{App}] {\Gamma\vdash M\,N:\tau_2} {\Gamma\vdash M:\tau_1\to\tau_2 \quad \Gamma\vdash N:\tau_1} $$

(Bool‐True)
$$ \infer[\textsc{True}] {\Gamma\vdash \mathbf{true}:\mathbf{Bool}} {\,} $$

(Nat‐Succ)
$$ \infer[\textsc{Succ}] {\Gamma\vdash \mathrm{succ}(M):\mathbf{Nat}} {\Gamma\vdash M:\mathbf{Nat}} $$

These rules specify exactly which term‐type combinations are well‐formed.

1.2.4 Example: ill‐typed term
Consider the untyped λ‐term
Ω=(λx.xx)(λx.xx). \Omega = (\lambda x.\;x\;x)\;(\lambda x.\;x\;x).
In STLC this term is untypable: attempting to assign x:τ→τx:\tau\to\tau forces τ→τ=τ\tau\to\tau=\tau, no solution in the simply typed world. Thus ill‐typed programs are rejected at compile time.

  1. 3 Notation, Metatheory and Conventions

1.3.1 Binding, α‐equivalence, substitution
– We write λx.M\lambda x.\,M for abstraction; xx binds free occurrences in MM.
– M≡αNM\equiv_\alpha N denotes equality up to renaming of bound variables.
– Capture‐avoiding substitution is written M[N/x]M[N/x], replacing free xx in MM by NN.

1.3.2 Context operations
– Γ,x:τ\Gamma,x:\tau extends Γ\Gamma with a fresh binding.
– We assume weakening and exchange admissible: if Γ⊢M:τ\Gamma\vdash M:\tau, adding irrelevant bindings or permuting order does not invalidate it.

1.3.3 Metatheoretic properties
Two properties are central in typed λ‐calculi:

(Progress)
If ⌀⊢M:τ\varnothing\vdash M:\tau, then either MM is a value or there exists M′M' with M→M′M\to M'.

(Preservation) (a.k.a. “Subject Reduction”)
If Γ⊢M:τ\Gamma\vdash M:\tau and M→M′M\to M', then Γ⊢M′:τ\Gamma\vdash M':\tau.

Together, these yield type safety: well-typed programs do not go wrong.

1.3.4 Programming‐language example
Below is a tiny OCaml‐style AST and type-checker fragment for Booleans and natural numbers:

type ty = Bool | Nat | Arrow of ty * ty

type term =
  | Var of string
  | Abs of string * ty * term
  | App of term * term
  | True | False
  | Zero | Succ of term

exception TypeError of string

let rec lookup x ctx =
  try List.assoc x ctx
  with Not_found -> raise (TypeError ("Unbound var " ^ x))

let rec tc ctx = function
  | Var x         -> lookup x ctx
  | Abs(x, t1, m) ->
      let t2 = tc ((x,t1)::ctx) m in
      Arrow(t1, t2)
  | App(m,n)      ->
      (match tc ctx m with
        | Arrow(t11,t12) ->
            let t2 = tc ctx n in
            if t11 = t2 then t12
            else raise (TypeError "Arg type mismatch")
        | _ -> raise (TypeError "Expected function"))
  | True | False  -> Bool
  | Zero          -> Nat
  | Succ m ->
      if tc ctx m = Nat then Nat
      else raise (TypeError "succ expects Nat")

This checker follows exactly the rules of section 1.2.3.

1.3.5 Chapter summary and road‐map
We have seen:

– The idea of types as classifications ensuring safety and expressivity.
– The formal machinery of contexts, judgments, and inference rules.
– Notational conventions (binding, substitution, α–equivalence).
– Metatheoretic goals: progress & preservation.

In Chapter 2 we anchor these ideas in classical mathematical structures—sets, relations and algebraic models—and show how types arise from familiar constructions in set theory and universal algebra.

Chapter 2 Mathematical Foundations: Set Theory and Algebra

In this chapter we review the classical mathematical notions of sets, relations and functions, then introduce the algebraic‐structure concepts (monoids, lattices, universal algebras) that underlie many type‐theoretic constructions. Finally, we show how data types in programming languages correspond to set‐ and algebraic‐theoretic constructs.

  1. 1 Sets, Relations and Functions

  2. 1.1 Sets and Set‐Operations
    A set AA is an unordered collection of distinct elements. We write x∈Ax\in A to mean “xx is a member of AA,” and A⊆BA\subseteq B to mean “every element of AA is also in BB.”

Standard operations on sets A,BA,B: A∪B={x∣x∈A or x∈B},A∩B={x∣x∈A and x∈B}, A\cup B = \{\,x \mid x\in A \text{ or } x\in B\}, \quad A\cap B = \{\,x \mid x\in A \text{ and } x\in B\}, A\B={x∣x∈A and x∉B},𝒫(A)={X∣X⊆A}. A\setminus B = \{\,x \mid x\in A \text{ and } x\notin B\}, \quad \mathcal P(A) = \{\,X \mid X\subseteq A\}. The cardinality |A||A| of a finite set AA is the number of its elements. For infinite sets one distinguishes countable vs. uncountable, but in type theory we often treat all sets extensionally.

  1. 1.2 Relations
    A binary relation RR between AA and BB is a subset R⊆A×B. R \;\subseteq\; A\times B. We write aRba\,R\,b as shorthand for (a,b)∈R(a,b)\in R. Important special cases:

Domain dom(R)={a∣∃b.aRb}\mathrm{dom}(R)=\{\,a\mid \exists b.\;a\,R\,b\}.
Range ran(R)={b∣∃a.aRb}\mathrm{ran}(R)=\{\,b\mid \exists a.\;a\,R\,b\}.

Given relations R⊆A×BR\subseteq A\times B and S⊆B×CS\subseteq B\times C, their composition is S∘R={(a,c)∣∃b.aRb∧bSc}⊆A×C. S\circ R \;=\; \{\,(a,c)\mid \exists b.\;a\,R\,b \,\wedge\, b\,S\,c\}\;\subseteq\;A\times C. The inverse or converse of RR is R−1={(b,a)∣(a,b)∈R}⊆B×A. R^{-1} \;=\;\{(b,a)\mid (a,b)\in R\}\;\subseteq\;B\times A.

Key relation properties on AA (R⊆A×AR\subseteq A\times A):

Reflexive: ∀a∈A.aRa\forall a\in A.\;a\,R\,a.
Symmetric: ∀a,b.aRb⟹bRa\forall a,b.\;a\,R\,b\implies b\,R\,a.
Transitive: ∀a,b,c.(aRb∧bRc)⟹aRc\forall a,b,c.\;(a\,R\,b\wedge b\,R\,c)\implies a\,R\,c.

An equivalence relation is reflexive, symmetric and transitive; it partitions AA into equivalence classes. A partial order is reflexive, antisymmetric (aRb∧bRa⟹a=ba\,R\,b\wedge b\,R\,a\implies a=b) and transitive.

  1. 1.3 Functions
    A function f:A→Bf\colon A\to B is a relation that is both ∀a∈A.∃!b∈B.(a,b)∈f. \forall a\in A.\;\exists!\;b\in B.\;(a,b)\in f. Equivalently, ff is a mapping a↦f(a)a\mapsto f(a). We say:

Injective (one‐to‐one): f(a)=f(a′)⟹a=a′f(a)=f(a')\implies a=a'.
Surjective (onto): ∀b∈B.∃a.f(a)=b\forall b\in B.\;\exists a.\;f(a)=b.
Bijective: both injective and surjective.

Composition of functions g∘f:A→Cg\circ f\colon A\to C is defined pointwise: (g∘f)(a)=g(f(a))(g\circ f)(a)=g(f(a)). The identity function on AA is 𝑖𝑑A(a)=a\mathit{id}_A(a)=a.

  1. 2 Algebraic Structures

  2. 2.1 Signatures and Universal Algebras
    A signature Σ=(S,𝑎𝑟)\Sigma=(S,\mathit{ar}) consists of a set SS of operation symbols and an arity function 𝑎𝑟:S→ℕ\mathit{ar}:S\to\mathbb{N} assigning each symbol its number of arguments.

A Σ\Sigma‐algebra 𝒜\mathcal A comprises: • A carrier set AA.
• For each f∈Sf\in S with 𝑎𝑟(f)=n\mathit{ar}(f)=n, an operation
f𝒜:An→A.f^{\mathcal A}:A^n\to A.

A homomorphism h:𝒜→ℬh\colon \mathcal A\to\mathcal B between two Σ\Sigma‐algebras is a function h:A→Bh\colon A\to B preserving every operation: h(f𝒜(a1,…,an))=fℬ(h(a1),…,h(an)). h\bigl(f^{\mathcal A}(a_1,\dots,a_n)\bigr) \;=\; f^{\mathcal B}\bigl(h(a_1),\dots,h(a_n)\bigr). An initial algebra in the class of Σ\Sigma‐algebras is one from which there is a unique homomorphism into any other—this models inductive (free) data types.

  1. 2.2 Monoids
    A monoid is a pair (M,*)(M,\ast) where MM is a set and *:M×M→M \ast\colon M\times M\to M is an associative binary operation with an identity element e∈Me\in M: ∀x,y,z∈M.(x*y)*z=x*(y*z),∀x.e*x=x=x*e. \forall x,y,z\in M.\;(x\ast y)\ast z = x\ast(y\ast z), \quad \forall x.\;e\ast x = x = x\ast e.

Examples:
– (ℕ,+)(\mathbb{N},+) with 00 as identity.
– Strings over an alphabet Σ\Sigma: (Σ*,𝑐𝑜𝑛𝑐𝑎𝑡,ε)(\Sigma^*,\mathit{concat},\varepsilon).

In Haskell one captures this as:

class Monoid m where
  mempty  :: m
  mappend :: m -> m -> mappend
  -- laws (not enforced by compiler):
  --  mappend mempty x = x
  --  mappend x mempty = x
  --  mappend x (mappend y z) = mappend (mappend x y) z
  1. 2.3 Lattices
    A lattice is a set LL equipped with two binary operations, ∨,∧:L×L→L, \vee,\wedge \colon L\times L\to L, called join and meet, satisfying for all x,y,z∈Lx,y,z\in L:

  2. Commutativity: x∨y=y∨xx\vee y = y\vee x, x∧y=y∧xx\wedge y = y\wedge x.

  3. Associativity: (x∨y)∨z=x∨(y∨z)(x\vee y)\vee z = x\vee(y\vee z), etc.

  4. Absorption: x∨(x∧y)=xx\vee(x\wedge y)=x, x∧(x∨y)=xx\wedge(x\vee y)=x.

Equivalently, (L,≤)(L,\le) is a partial order in which every pair has a least upper bound (join) and greatest lower bound (meet). Example: (𝒫(A),⊆)(\mathcal P(A),\subseteq) with X∨Y=X∪Y,X∧Y=X∩Y. X\vee Y = X\cup Y, \quad X\wedge Y = X\cap Y.

  1. 3 Types as Sets and Algebraic Data Types

  2. 3.1 Types as Sets of Values
    In a denotational semantics one assigns to each type τ\tau a set of values |τ||\tau|. The usual connectives correspond to set‐theoretic constructions:

• Product type:
|τ1×τ2|=|τ1|×|τ2|.|\tau_1\times\tau_2| \;=\; |\tau_1|\times|\tau_2|.
• Sum type (disjoint union):
|τ1+τ2|=|τ1|⊎|τ2|.|\tau_1+\tau_2| \;=\; |\tau_1|\;\uplus\;|\tau_2|.
• Function type:
|τ1→τ2|=|τ2||τ1|.|\tau_1\to\tau_2| \;=\; |\tau_2|^{\,|\tau_1|}.

Cardinalities satisfy |τ1×τ2|=|τ1|⋅|τ2|,|τ1+τ2|=|τ1|+|τ2|. |\tau_1\times\tau_2| = |\tau_1|\cdot|\tau_2|, \quad |\tau_1+\tau_2| = |\tau_1| + |\tau_2|.

  1. 3.2 Algebraic (Inductive) Data Types
    An algebraic data type is given by constructors whose arities form a signature; the type is interpreted as the initial algebra of that signature. For example, in Haskell:
data Maybe a = Nothing | Just a

Signature: S={𝑁𝑜𝑡h𝑖𝑛𝑔,𝐽𝑢𝑠𝑡}S=\{\mathit{Nothing},\,\mathit{Just}\} with 𝑎𝑟(𝑁𝑜𝑡h𝑖𝑛𝑔)=0\mathit{ar}(\mathit{Nothing})=0, 𝑎𝑟(𝐽𝑢𝑠𝑡)=1\mathit{ar}(\mathit{Just})=1. Its carrier |𝑀𝑎𝑦𝑏𝑒A|={⊥}∪A|\mathit{Maybe}\,A|=\{\bot\}\cup A.

List example:

data List a = Nil | Cons a (List a)

Signature Σ𝐿𝑖𝑠𝑡\Sigma_{\mathit{List}}:
– 𝑁𝑖𝑙\mathit{Nil} of arity 00.
– 𝐶𝑜𝑛𝑠\mathit{Cons} of arity 22.

|𝐿𝑖𝑠𝑡A||\mathit{List}\,A| is the smallest set LL such that $$ \Nil\in L \quad\text{and}\quad (x\in A,\;xs\in L)\;\Rightarrow\;\Cons(x,xs)\in L. $$

  1. 3.3 Example: Binary Trees
    In OCaml:
type 'a tree =
  | Leaf of 'a
  | Node of 'a tree * 'a tree

The signature has two constructors: – 𝐿𝑒𝑎𝑓:A→T\mathit{Leaf}\colon A\to T.
– 𝑁𝑜𝑑𝑒:T×T→T\mathit{Node}\colon T\times T\to T.

By universal‐algebra, TT is the initial Σ\Sigma‐algebra, so every other Σ\Sigma‐algebra receives a unique homomorphism from TT. This underpins fold‐like recursion schemes.

  1. 3.4 Cardinality and Infinity
    If AA is infinite then so are many constructed types (e.g. AnA^n, A*A^*). Recursive types may denote least fixed points of functors F(X)=1+A×X, F(X) = 1 + A\times X, with solution X=μF≅⨄n≥0AnX = \mu F \cong \biguplus_{n\ge0} A^n (the set of finite lists).

  2. 4 Chapter Summary

– We recalled set‐theoretic notation: unions, intersections, power sets, relations and functions (injective/surjective/bijective).
– We introduced universal‐algebraic signatures, Σ\Sigma‐algebras, homomorphisms and initial algebras.
– We defined monoids and lattices, key structures in both mathematics and program‐analysis (e.g. type lattices).
– We showed how programming‐language types correspond to set constructs (products, sums, exponentials) and to initial algebras of signatures (inductive types).

In Chapter 3 we will enrich this picture by showing how types and terms form categories, how simply typed λ\lambda‐calculus lives in a cartesian closed category, and how monads capture computational effects.

Chapter 3 Category Theory and Types

In this chapter we introduce the basic notions of category theory—categories, functors and natural transformations—and show how they provide an elegant framework for understanding type theory. We then explain Cartesian closed categories (CCCs) and their exact correspondence with the simply‐typed λ‐calculus. Finally, we present monads (and comonads) as categorical abstractions of computational effects.

  1. 1 Categories, Functors and Natural Transformations

  2. 1.1 Definition of a Category
    A category 𝒞\mathcal{C} consists of: • A class of objects Ob(𝒞)\mathrm{Ob}(\mathcal{C}).
    • For each A,B∈Ob(𝒞)A,B\in \mathrm{Ob}(\mathcal{C}), a set of morphisms (or arrows)
    𝒞(A,B)={f:A→B}.\mathcal{C}(A,B)\;=\;\{\,f\colon A\to B\,\}.
    • For each A,B,CA,B,C, a composition operation
    ∘:𝒞(B,C)×𝒞(A,B)→𝒞(A,C),\circ\colon \mathcal{C}(B,C)\times\mathcal{C}(A,B)\;\to\;\mathcal{C}(A,C),
    written (g,f)↦g∘f(g,f)\mapsto g\circ f.
    • For each object AA, an identity morphism
    idA:A→A.\mathrm{id}_A\colon A\to A.

These data satisfy the laws:

(Cat‐Assoc) (h∘g)∘f=h∘(g∘f)\quad (h\circ g)\circ f \;=\; h\circ (g\circ f)

(Cat‐Id) idB∘f=f=f∘idA\quad \mathrm{id}_B\circ f = f = f\circ \mathrm{id}_A

for all composable f,g,hf,g,h.

  1. 1.2 Examples of Categories

Set: objects are all sets; morphisms are total functions.
Hask: in Haskell, types as objects, total (pure) Haskell functions as morphisms (ignoring bottoms).
Rel: objects are sets; morphisms R:A→BR\colon A\to B are binary relations R⊆A×BR\subseteq A\times B, composed by relational composition.

  1. 1.3 Functors
    A functor F:𝒞→𝒟F\colon \mathcal{C}\to\mathcal{D} between categories assigns:

• To each object A∈Ob(𝒞)A\in\mathrm{Ob}(\mathcal{C}), an object F(A)∈Ob(𝒟)F(A)\in\mathrm{Ob}(\mathcal{D}).
• To each morphism f:A→Bf\colon A\to B, a morphism
F(f):F(A)→F(B)F(f)\colon F(A)\to F(B)

preserving identities and composition:

F(idA)=idF(A),F(g∘f)=F(g)∘F(f). F(\mathrm{id}_A) = \mathrm{id}_{F(A)}, \quad F(g\circ f) = F(g)\circ F(f).

Example. The power‐set functor 𝒫:𝐒𝐞𝐭→𝐒𝐞𝐭\mathcal P\colon \mathbf{Set}\to\mathbf{Set} sends A↦𝒫(A),f:A→B↦𝒫(f):𝒫(A)→𝒫(B),𝒫(f)(X)={f(x)∣x∈X}. A \;\mapsto\; \mathcal P(A), \quad f\colon A\to B \;\mapsto\; \mathcal P(f)\colon \mathcal P(A)\to\mathcal P(B), \quad \mathcal P(f)(X)=\{\,f(x)\mid x\in X\}.

  1. 1.4 Natural Transformations
    Given parallel functors F,G:𝒞→𝒟F,G\colon \mathcal{C}\to\mathcal{D}, a natural transformation
    η:F⇒G\eta\colon F\Rightarrow G
    assigns to each A∈Ob(𝒞)A\in\mathrm{Ob}(\mathcal{C}) a morphism
    ηA:F(A)→G(A)\eta_A\colon F(A)\to G(A)
    such that for every f:A→Bf\colon A\to B in 𝒞\mathcal{C} the following naturality square commutes: $$ \begin{CD} F(A) @>F(f)>> F(B) \\ @V\eta_AVV @VV\eta_B V \\ G(A) @>G(f)>> G(B) \end{CD} $$

Example. In Set, the inclusion ι:X↪𝒫(X)\iota\colon X\hookrightarrow \mathcal P(X) (sending x↦{x}x\mapsto\{x\}) is a natural transformation Id⇒𝒫\mathrm{Id}\Rightarrow\mathcal P.

  1. 2 Cartesian Closed Categories ↔\longleftrightarrow Simply‐Typed λ‐Calculus

  2. 2.1 Products and Terminal Object
    A category 𝒞\mathcal{C} has a terminal object 11 if for every AA there is a unique morphism
    !A:A→1.!_A\colon A\to 1. A product of A,BA,B is an object A×BA\times B equipped with projections
    π1:A×B→A,π2:A×B→B\pi_1\colon A\times B\to A,\quad \pi_2\colon A\times B\to B
    universal in the sense that for any CC and f:C→Af\colon C\to A, g:C→Bg\colon C\to B, there is a unique
    ⟨f,g⟩:C→A×B\langle f,g\rangle\colon C\to A\times B
    making π1∘⟨f,g⟩=f\pi_1\circ\langle f,g\rangle = f and π2∘⟨f,g⟩=g\pi_2\circ\langle f,g\rangle = g.

  3. 2.2 Exponentials
    Given objects A,BA,B, an exponential BAB^A (if it exists) is equipped with an evaluation morphism
    ev:BA×A→B\mathrm{ev}\colon B^A\times A \;\to\; B
    such that for every f:C×A→Bf\colon C\times A\to B there is a unique
    Λ(f):C→BA\Lambda(f)\colon C\;\to\; B^A
    making the diagram commute: $$ \begin{CD} C\times A @>f>> B \\ @V\Lambda(f)\times \mathrm{id}_A VV @| \\ B^A\times A @>\mathrm{ev}>> B \end{CD} $$

  4. 2.3 Definition of a Cartesian Closed Category (CCC)
    A category is cartesian closed if it has:

  5. A terminal object 11.

  6. Binary products A×BA\times B for all A,BA,B.

  7. Exponentials BAB^A for all A,BA,B.

  8. 2.4 CCC ↔︎ Simply‐Typed λ‐Calculus
    There is an equivalence:

• In the simply‐typed λ‐calculus (STLC), types τ\tau are objects, contexts Γ\Gamma give products, and terms M:Γ⊢τM\colon \Gamma\vdash\tau correspond to morphisms
M:|Γ|=∏x:σ∈Γσ→τ.M\colon |\Gamma|\;=\;\prod_{x:\sigma\in\Gamma}\sigma\;\longrightarrow\;\tau. • Composition of morphisms models term substitution; identity morphisms are variables.
• λ\lambda‐abstraction corresponds to Λ\Lambda (currying), application to ev\mathrm{ev} (evaluation).

Thus the category 𝐒𝐓𝐋𝐂\mathbf{STLC} of types-and-terms is a CCC. Conversely, every CCC gives rise to a typed λ‐calculus.

  1. 2.5 Curry–Howard–Lambek Correspondence
    The classic trinity:

• Propositional logic with ×,→\times,\to.
• Simply‐typed λ‐calculus with product types and function types.
• Cartesian closed categories.

Under this, proof‐derivation = λ‐term = morphism.

  1. 3 Monads, Comonads and Computational Effects

  2. 3.1 Definition of a Monad
    A monad on a category 𝒞\mathcal{C} is a triple (T,η,μ)(T,\eta,\mu) where
    • T:𝒞→𝒞T\colon \mathcal{C}\to\mathcal{C} is an endofunctor.
    • η:Id⇒T\eta\colon \mathrm{Id}\Rightarrow T (unit) and μ:T2⇒T\mu\colon T^2\Rightarrow T (multiplication) are natural transformations satisfying:

    Associativity:
    μA∘T(μA)=μA∘μT(A). \mu_A\circ T(\mu_A) = \mu_A\circ \mu_{T(A)}.

    Unit laws:
    μA∘T(ηA)=idT(A),μA∘ηT(A)=idT(A). \mu_A\circ T(\eta_A) = \mathrm{id}_{T(A)} \;,\quad \mu_A\circ \eta_{T(A)} = \mathrm{id}_{T(A)}.

  3. 3.2 Kleisli and Eilenberg–Moore
    – The Kleisli category 𝒞T\mathcal{C}_T: same objects as 𝒞\mathcal{C}, morphisms A→BA\to B are A→T(B)A\to T(B) in 𝒞\mathcal{C}.
    Kleisli extension (−)*(-)^\ast: for f:A→T(B)f\colon A\to T(B), obtain f*:T(A)→T(B)f^\ast\colon T(A)\to T(B).

  4. 3.3 Examples of Monads

Maybe monad in Hask:
T(A)=A+1,η(a)=𝖩𝗎𝗌𝗍a,μ(𝖩𝗎𝗌𝗍x)=x,μ(𝖭𝗈𝗍𝗁𝗂𝗇𝗀)=𝖭𝗈𝗍𝗁𝗂𝗇𝗀.T(A)=A+1,\quad \eta(a)=\mathsf{Just}\;a,\quad \mu(\mathsf{Just}\,x)=x,\;\mu(\mathsf{Nothing})=\mathsf{Nothing}. – List monad: T(A)=A*T(A)=A^*, η(a)=[a]\eta(a)=[a], μ=𝖼𝗈𝗇𝖼𝖺𝗍\mu=\mathsf{concat}.
State monad: T(A)=S→(A×S)T(A)=S\to (A\times S).

  1. 3.4 Haskell Example

+  - Maybe monad
instance Monad Maybe where
  return x        = Just x
  Nothing  >>= _  = Nothing
  (Just x) >>= k  = k x


+  - List monad
instance Monad [] where
  return x = [x]
  xs >>= k = concat (map k xs)
  1. 3.5 Monads in Semantics of Computation
    Moggi’s insight: monads encapsulate computational effects—state, exceptions, nondeterminism, I/O—while preserving a pure core. One endows a “computational λ‐calculus” with type constructor T(τ)T(\tau) for effectful computations.

  2. 3.6 Comonads
    Dually, a comonad is (W,ε,δ)(W,\varepsilon,\delta) with W:𝒞→𝒞W\colon\mathcal{C}\to\mathcal{C}, counit ε:W⇒Id\varepsilon\colon W\Rightarrow\mathrm{Id}, comultiplication δ:W⇒W2\delta\colon W\Rightarrow W^2 satisfying dual laws. The coKleisli category models context‐dependent computations.

  3. 3.7 Example: Reader Comonad
    In a language with a fixed environment EE, define
    W(A)=E→A,ε(f)=f(e0),δ(f)(e)=λe′.f(e′).W(A)=E\to A,\quad \varepsilon(f)=f(e_0),\quad \delta(f)(e)=\lambda e'.\,f(e').
    This comonad represents read‐only context passing.

  4. 4 Chapter Summary

– We defined categories, functors and natural transformations.
– We showed that Cartesian closed categories precisely model the simply‐typed λ‐calculus.
– We introduced monads as categorical abstractions of computational effects and sketched Kleisli semantics.
– We briefly sketched comonads for context‐sensitive computations.

In Chapter 4 we turn to combinatory logic—an alternative variable‐free foundation of computation—and relate its typed variants back to the λ‐calculus and categorical models.

Chapter 4 Combinatory Logic and the Foundations of Typing

In this chapter we present Combinatory Logic (CL), a variable‐free formulation of computation invented by Schönfinkel and Curry. We introduce the basic SKI system, develop its simply‐typed variant, and prove its equivalence with the simply‐typed λ‐calculus. Along the way we show how combinatory reduction can be implemented in practice and how combinatory algebras arise as categorical models.

  1. 1 The Untyped SKI Combinators

  2. 1.1 Syntax and Reduction Rules
    Terms of the SKI‐calculus are built from three primitive combinators 𝐒,𝐊,𝐈\mathbf{S},\mathbf{K},\mathbf{I} and application: M,N::=𝐒∣𝐊∣𝐈∣MN M,N ::= \mathbf{S}\mid \mathbf{K}\mid \mathbf{I} \mid M\;N Reduction is effected by the rules: 𝐊MN→M,𝐒MNP→MP(NP),𝐈M→M. \begin{aligned} \mathbf{K}\;M\;N &\;\to\; M,\\ \mathbf{S}\;M\;N\;P &\;\to\; M\;P\;(N\;P),\\ \mathbf{I}\;M &\;\to\; M. \end{aligned} Here application associates to the left: MNPQ≡(((MN)P)Q)MNPQ\equiv(((M\,N)\,P)\,Q).

  3. 1.2 Expressiveness: Eliminating Variables
    Every λ‐term tt can be translated into an equivalent SKI‐term [t][t] satisfying [λx.t]N→*t[N/x]. [\lambda x.\,t]\;N \;\to^*\; t[N/x]. The usual translation [[⋅]][\![\,\cdot\,]\!] is defined by:

  4. [[x]]=x[\![x]\!] = x (treated as a nullary combinator or variable).

  5. [[MN]]=[[M]][[N]][\![M\,N]\!] = [\![M]\!]\;[\![N]\!].

  6. [[λx.M]]=𝐊[[M]][\![\lambda x.\,M]\!] = \mathbf{K}\,[\![M]\!] if x∉𝐹𝑉(M)x\notin\mathit{FV}(M);

  7. [[λx.x]]=𝐈[\![\lambda x.\,x]\!] = \mathbf{I};

  8. [[λx.MN]]=𝐒[[λx.M]][[λx.N]][\![\lambda x.\,M\,N]\!] = \mathbf{S}\,[\![\lambda x.\,M]\!]\,[\![\lambda x.\,N]\!].

  9. 1.3 Implementation: A Simple C Reducer
    Below is a sketch of a C program that represents combinators and performs head‐reduction.

// skicomb.c
#include <stdio.h>
#include <stdlib.h>

typedef enum { S_COMB, K_COMB, I_COMB, APP } Tag;

typedef struct Term Term;
struct Term {
  Tag tag;
  Term *left, *right;
};

Term* make(Tag t, Term* l, Term* r) {
  Term* p = malloc(sizeof *p);
  p->tag = t; p->left = l; p->right = r;
  return p;
}

Term* reduce(Term* t) {
  if (t->tag==APP && t->left->tag==K_COMB) {
    // K M N → M
    return t->left->right;
  }
  if (t->tag==APP && t->left->tag==APP &&
      t->left->left->tag==S_COMB) {
    // S M N P → M P (N P)
    Term *M = t->left->right;
    Term *N = t->right;
    Term *P = t->right; // careful: P = t->right
    Term *NP = make(APP,N,P);
    return make(APP, make(APP,M,P), NP);
  }
  if (t->tag==APP && t->left->tag==I_COMB) {
    // I M → M
    return t->right;
  }
  // no rule applies
  return t;
}

int main(){
  // Example: ((S K) K) x reduces to x
  Term *x = make(APP, make(K_COMB,0,0), make(K_COMB,0,0));
  // omitted full build / loop
}
  1. 2 Simply‐Typed Combinatory Calculus

  2. 2.1 Types and Typing Rules
    We decorate SKI with simple types τ\tau generated by τ::=α∣τ1→τ2, \tau ::= \alpha\mid \tau_1\to\tau_2, where α\alpha ranges over base‐type atoms. Typing judgments have the form ⊢M:τ. \vdash M:\tau. We impose the following rules:

(Var)
$$ \infer{\vdash x:\tau}{x:\tau\in\Gamma} $$

  1. $$ \infer{\vdash \mathbf{K}:\tau_1\to\tau_2\to\tau_1}{} $$

  2. $$ \infer{\vdash \mathbf{S}:(\tau_1\to\tau_2\to\tau_3)\to(\tau_1\to\tau_2)\to \tau_1\to\tau_3}{} $$

  3. $$ \infer{\vdash \mathbf{I}:\tau\to\tau}{} $$

(App)
$$ \infer{\vdash M\;N:\tau_2} {\vdash M:\tau_1\to\tau_2 \quad \vdash N:\tau_1} $$

  1. 2.2 Example Derivation
    Prove ⊢𝐒𝐊𝐊:α→α\vdash \mathbf{S}\,\mathbf{K}\,\mathbf{K}:\alpha\to\alpha.

  2. By (K), ⊢𝐊:α→β→α\vdash \mathbf{K}:\alpha\to\beta\to\alpha.

  3. By (K) (instantiating β=α\beta=\alpha), ⊢𝐊:α→α→α\vdash \mathbf{K}:\alpha\to\alpha\to\alpha.

  4. By (S),
    ⊢𝐒:(α→α→α)→(α→α)→α→α\vdash \mathbf{S}:(\alpha\to\alpha\to\alpha)\to(\alpha\to\alpha)\to\alpha\to\alpha.

  5. Apply (App) twice to obtain
    ⊢𝐒𝐊𝐊:α→α\vdash \mathbf{S}\,\mathbf{K}\,\mathbf{K}:\alpha\to\alpha.

  6. 3 Equivalence with Simply‐Typed λ‐Calculus

  7. 3.1 Translation from CL to λ‐Calculus
    Define Φ(M)\Phi(M) as the λ‐term corresponding to CL‐term MM by mapping Φ(𝐒)=λxyz.xz(yz),Φ(𝐊)=λxy.x,Φ(𝐈)=λx.x, \Phi(\mathbf{S}) = \lambda x y z.\;x\,z\,(y\,z), \quad \Phi(\mathbf{K}) = \lambda x y.\;x, \quad \Phi(\mathbf{I}) = \lambda x.\;x, and Φ(MN)=Φ(M)Φ(N)\Phi(M\,N)=\Phi(M)\,\Phi(N). One shows:

Theorem (Soundness)
If ⊢CLM:τ\vdash_{\mathrm{CL}} M:\tau then ⊢λΦ(M):τ\vdash_{\lambda} \Phi(M):\tau.

  1. 3.2 Translation from λ‐Calculus to CL
    Conversely, map each λ‐abstraction to SKI via the translation of §4.1.2. Then:

Theorem (Completeness)
If Γ⊢λt:τ\Gamma\vdash_{\lambda} t:\tau then ⊢CL[[t]]:τ\vdash_{\mathrm{CL}} [\![t]\!]:\tau.

  1. 3.3 Adequacy of Translations
    Combining soundness and completeness yields an isomorphism of derivability and preserved reduction: [[Φ(M)]]→CL*M,Φ([[t]])→λ*t. [\![\Phi(M)]\!] \;\to^*_{\mathrm{CL}}\; M, \quad \Phi([\![t]\!]) \;\to^*_{\lambda}\; t.

  2. 4 Combinatory Algebras and Categorical Models

  3. 4.1 Combinatory Algebra
    A combinatory algebra is a set AA with a binary application operation ⋅\cdot and distinguished elements s,k∈As,k\in A satisfying:

  4. s⋅x⋅y⋅z=x⋅z⋅(y⋅z)s\cdot x\cdot y\cdot z = x\cdot z\cdot (y\cdot z)

  5. k⋅x⋅y=xk\cdot x\cdot y = x

Such an algebra models CL and yields a categorical model by taking objects as types and morphisms A→BA\to B as the set {f∈A∣∀x∈A,f⋅x∈B}. \{\,f\in A \mid \forall x\in A,\;f\cdot x\in B\}.

  1. 4.2 Categorical Perspective
    Every CCC yields a combinatory algebra by collapsing the CCC’s morphisms into an applicative structure. Conversely, from a combinatory algebra with enough “abstract” points one constructs a CCC of partial equivalence relations.

  2. 5 Programming with Combinators

  3. 5.1 Scheme Example: Combinator Interpreter

(define (I x) x)
(define (K x) (lambda (y) x))
(define (S x) (lambda (y) (lambda (z)
                ((x z) ((y z))))))
                
(define (eval-sk m)
  (cond ((procedure? m) m)
        ((pair? m)     ; application
         (let ((f (eval-sk (car m)))
               (a (eval-sk (cadr m))))
           (f a)))
        (else (error "Unknown term" m))))
  1. 5.2 Haskell Example: SKI as Data Type
data SKI = S | K | I | App SKI SKI

reduce :: SKI -> SKI
reduce (App (App (App S m) n) p) = App (App m p) (App n p)
reduce (App (App K m) _)       = m
reduce (App I m)               = m
reduce t                       = t
  1. 6 Chapter Summary

– We introduced the untyped SKI combinators and showed how they eliminate variables.
– We equipped SKI with simple types and gave typing rules for 𝐒,𝐊,𝐈\mathbf{S},\mathbf{K},\mathbf{I}.
– We proved equivalence with the simply‐typed λ‐calculus via bidirectional translations.
– We outlined the notion of a combinatory algebra and its connection to categorical models.
– We provided concrete implementations in C, Scheme and Haskell.

In Chapter 5 we return to the λ‐calculus itself, formalize its operational semantics and develop richer type systems including products, sums and recursive types.

Chapter 5 Lambda Calculus and Typed Lambda Calculi

In this chapter we develop the untyped λ‐calculus—the foundational model of computation—and then introduce its typed variants. We cover syntax, α/β/η‐conversion, operational semantics, Church encodings of data, the simply‐typed λ‐calculus (STLC), principal‐type inference (Hindley–Milner / Algorithm W), and an introduction to second‐order polymorphism (System F).

  1. 1 The Untyped λ‐Calculus

  2. 1.1 Syntax
    Terms M,NM,N are given by the grammar M,N::=x∣λx.M∣MN M,N ::= x \mid \lambda x.\,M \mid M\,N where xx ranges over a countable set of variables.

  3. 1.2 α‐Conversion and Capture‐Avoiding Substitution
    – α‐equivalence (M≡αNM\equiv_\alpha N) identifies renamings of bound variables.
    – Capture‐avoiding substitution M[N/x]M[N/x] replaces free occurrences of xx in MM by NN, renaming bound variables in MM as needed to avoid capture.

  4. 1.3 β‐Reduction and η‐Reduction
    – β‐reduction: (λx.M)N→βM[N/x](\lambda x.\,M)\,N \;\to_\beta\; M[N/x].
    – η‐reduction: λx.Mx→ηM\lambda x.\,M\,x \;\to_\eta\; M when x∉𝐹𝑉(M)x\notin\mathit{FV}(M).
    We write →*\to^* for the reflexive‐transitive closure of →β∪→η\to_\beta\cup\to_\eta.

  5. 1.4 Operational Semantics and Strategies
    Normal‐order (leftmost‐outermost) vs applicative‐order (leftmost‐innermost).
    – Head‐reduction computes head‐normal form if it exists.

  6. 1.5 Church Encodings of Data

  7. 1.5.1 Booleans
    𝗍𝗋𝗎𝖾=λtf.t,𝖿𝖺𝗅𝗌𝖾=λtf.f. \mathsf{true} = \lambda t f.\,t,\quad \mathsf{false} = \lambda t f.\,f. Define 𝗂𝖿M𝗍𝗁𝖾𝗇N𝖾𝗅𝗌𝖾P=MNP\mathsf{if}\;M\;\mathsf{then}\;N\;\mathsf{else}\;P = M\,N\,P.

  8. 1.5.2 Church Numerals
    n¯=λsz.snz, \overline{n} = \lambda s z.\;s^n\,z, with successor and addition: 𝗌𝗎𝖼𝖼=λnsz.s(nsz),𝗉𝗅𝗎𝗌=λmnsz.ms(nsz). \mathsf{succ} = \lambda n s z.\,s\,(n\,s\,z),\quad \mathsf{plus} = \lambda m n s z.\,m\,s\,(n\,s\,z).

  9. 1.5.3 Pairs
    ⟨M,N⟩=λp.pMN,𝖿𝗌𝗍=λp.p(λxy.x),𝗌𝗇𝖽=λp.p(λxy.y). \langle M,N\rangle = \lambda p.\,p\,M\,N,\quad \mathsf{fst} = \lambda p.\,p\,(\lambda x y.\,x),\quad \mathsf{snd} = \lambda p.\,p\,(\lambda x y.\,y).

  10. 1.6 Example: A Small Evaluator in Scheme

;; untyped λ‐calculus evaluator (very naive)
(define (subst body x val)
  (match body
    [(var y)      (if (eq? y x) val body)]
    [(lam y m)    (if (eq? y x) body
                    (lam y (subst m x val)))]
    [(app m n)    (app (subst m x val) (subst n x val))]))

(define (step term)
  (match term
    [(app (lam x m) n)       (subst m x n)]   ; β‐reduction
    [(app m n)               (app (step m) n)]
    [else                    term]))

(define (eval term)
  (let loop ([t term])
    (let ([t' (step t)])
      (if (eqv? t t') t (loop t')))))
  1. 2 Simply‐Typed λ‐Calculus (STLC)

  2. 2.1 Types and Terms
    Types τ\tau and terms MM are given by τ::=α∣τ1→τ2,M::=x∣λx:τ.M∣MN. \tau ::= \alpha \mid \tau_1\to\tau_2, \qquad M ::= x \mid \lambda x\!:\!\tau.\,M \mid M\,N.

  3. 2.2 Typing Contexts and Judgments
    A context Γ\Gamma is a finite map from variables to types. Typing judgment: Γ⊢M:τ. \Gamma\vdash M:\tau.

  4. 2.3 Inference Rules
    [ {x!:!} {,x!:!_1;;M:_2} ] [ {M:_1_2 N:_1} ]

  5. 2.4 Type Safety
    The key metatheoretic properties are:

(Progress) If ⌀⊢M:τ\varnothing\vdash M:\tau then either MM is a value (λ\lambda‐abstraction) or M→M′M\to M'.

(Preservation) If Γ⊢M:τ\Gamma\vdash M:\tau and M→M′M\to M' then Γ⊢M′:τ\Gamma\vdash M':\tau.

Together they imply type safety (no stuck well‐typed terms).

  1. 2.5 Example Typing in OCaml
(* identity *)
let id (x : 'a) : 'a = x

(* composition *)
let compose (f : 'b -> 'c) (g : 'a -> 'b) (x : 'a) : 'c =
  f (g x)
  1. 3 Principal‐Type Inference: Hindley–Milner and Algorithm W

  2. 3.1 Type Schemes and Generalization
    – A type scheme is ∀α1…αn.τ\forall\alpha_1\dots\alpha_n.\,\tau.
    Generalization 𝗀𝖾𝗇(Γ,τ)\mathsf{gen}(\Gamma,\tau) quantifies over type variables in τ\tau not free in Γ\Gamma.

  3. 3.2 Algorithm W Outline
    Given Γ\Gamma and term MM, computes (S,τ)(S,\tau) where SS is a substitution and τ\tau a type such that SΓ⊢M:τS\Gamma\vdash M:\tau, and τ\tau is principal.

  4. 3.3 Example in Standard ML

(* val id = fn x => x *)
(* inferred type: 'a -> 'a *)
  1. 4 Polymorphic λ‐Calculus (System F)

  2. 4.1 Syntax and Types
    Types and terms: τ::=α∣τ1→τ2∣∀α.τ,M::=x∣λx:τ.M∣MN∣Λα.M∣M[τ]. \tau ::= \alpha \mid \tau_1\to\tau_2 \mid \forall\alpha.\,\tau, \quad M ::= x \mid \lambda x\!:\!\tau.\,M \mid M\,N \mid \Lambda\alpha.\,M \mid M\,[\tau].

  3. 4.2 Typing Rules
    [ {M: ()} {M : .,} ]

  4. 4.3 Encodings and Expressiveness
    System F can type‐safe encode Church numerals, booleans, pairs, lists, etc., without ad hoc combinators.

  5. 5 Chapter Summary

– We defined the untyped λ‐calculus: syntax, α/β/η‐conversion, evaluation strategies, Church encodings.
– We introduced the simply‐typed λ‐calculus (STLC), its typing rules, and proved type safety (progress & preservation).
– We sketched Hindley–Milner principal‐type inference (Algorithm W) and gave ML/OCaml examples.
– We presented the polymorphic λ‐calculus (System F) with universal quantification.

In Chapter 6 we extend these calculi with inductive and coinductive types, explore pattern matching, and develop their categorical and semantic underpinnings.

Chapter 6 Subtyping and Record Types

In this chapter we extend the simply‐typed λ‐calculus with record types and introduce a subtyping relation to allow safe reuse of records with more fields or more specific field types. We cover

• Syntax and typing of record expressions
• The subtyping relation <:<:\,: reflexivity, transitivity, top type, arrow subtyping, and three forms of record subtyping (width, depth, permutation)
• Typing rules with subsumption
• Examples in OCaml and TypeScript
• Preservation and progress in the presence of subtyping

  1. 1 Motivation for Subtyping
    Many programming‐language features benefit from being able to treat a value of one type as a value of a “bigger” or “more general” type. In particular:
    – A record with extra fields can safely be used when only a subset of fields is needed (width subtyping).
    – A record whose field types are more specific can be used where a more general field type is expected (depth subtyping).
    – Fields may be listed in any order (permutation subtyping).

  2. 2 Syntax and Static Semantics

  3. 2.1 Types
    Extend types τ\tau by record types: τ::=α∣τ1→τ2∣{li:τi}i=1n∣⊤ \tau ::= \alpha \mid \tau_1\to\tau_2 \mid \{\;l_i:\tau_i\}_{i=1}^n \mid \top Here {li:τi}\{l_i:\tau_i\} is a record type with labels l1,…,lnl_1,\dots,l_n, and ⊤\top is the “top” type, supertype of all types.

  4. 2.2 Terms
    Extend terms MM by record formation and projection: M::=x∣λx:τ.M∣MN∣{li=Mi}i=1n∣M.l M ::= x \mid \lambda x\!:\!\tau.\,M \mid M\;N \mid \{\,l_i=M_i\}_{i=1}^n \mid M.l

  5. 2.3 Values
    Values vv are: v::=λx:τ.M∣{li=vi}i=1n. v ::= \lambda x\!:\!\tau.\,M \mid \{\,l_i=v_i\}_{i=1}^n.

  6. 3 The Subtyping Relation

We write S<:TS<:T when SS is a subtype of TT. The rules are:

(T‐Refl)
$$ \infer {T<:T} {} $$

(T‐Trans)
$$ \infer {S<:U} {S<:T \quad T<:U} $$

(T‐Top)
$$ \infer {S<:\top} {} $$

(T‐Arrow)
$$ \infer {S_1\to S_2<:T_1\to T_2} {T_1<:S_1 \quad S_2<:T_2} $$ Note: function types are contravariant in the domain and covariant in the codomain.

(T‐RcdWidth)
If {li:Si}i=1n\{\,l_i:S_i\}_{i=1}^n has at least the labels l1,…,lkl_1,\dots,l_k then $$ \infer {\{\,l_i:S_i\}_{i=1}^n \;<:\;\{\,l_i:S_i\}_{i=1}^k} {} $$

(T‐RcdDepth)
$$ \infer {\{\,l_i:S_i\}_{i=1}^n \;<:\;\{\,l_i:T_i\}_{i=1}^n} { \forall i.\;S_i<:T_i } $$

(T‐RcdPerm)
If (j1,…,jn)(j_1,\dots,j_n) is a permutation of (1,…,n)(1,\dots,n) then $$ \infer {\{\,l_{j_i}:S_{j_i}\}_{i=1}^n \;<:\;\{\,l_i:S_i\}_{i=1}^n} {} $$

Together, these let us drop fields (width), replace each field by a supertype (depth), and ignore ordering (permutation).

  1. 4 Typing Rules with Subsumption

Extend the standard STLC rules with record formation and projection, plus subsumption:

(Var)
$$ \infer {\Gamma\vdash x:\tau} {x:\tau\in\Gamma} $$

(Abs)
$$ \infer {\Gamma\vdash \lambda x:\!S.\,M : S\to T} {\Gamma,x:S\vdash M:T} $$

(App)
$$ \infer {\Gamma\vdash M\,N : T} {\Gamma\vdash M : S\to T \quad \Gamma\vdash N : S} $$

(Rcd)
$$ \infer {\Gamma\vdash \{\,l_i=M_i\}_{i=1}^n : \{\,l_i:\tau_i\}_{i=1}^n} {\forall i.\;\Gamma\vdash M_i:\tau_i} $$

(Proj)
$$ \infer {\Gamma\vdash M.l_j : \tau_j} {\Gamma\vdash M:\{\,l_i:\tau_i\}_{i=1}^n \quad j\le n} $$

(Subsumption)
$$ \infer {\Gamma\vdash M:S} {\Gamma\vdash M:T \quad T<:S} $$

  1. 5 Examples

  2. 5.1 OCaml Records and Width Subtyping (by Hand)
    (* OCaml does not support structural subtyping directly, but we illustrate the idea *)

type person   = { name : string; age : int }
type employee = { name : string; age : int; id : int }

let greet (p : person) =
  "Hello, " ^ p.name ^ " age " ^ string_of_int p.age

let e = { name="Alice"; age=30; id=123 }
let s = greet e   (* OK by width subtyping: employee <: person *)
  1. 5.2 TypeScript Structural Subtyping
interface Person { name: string; age: number }
interface Employee { name: string; age: number; id: number }

function greet(p: Person): string {
  return `Hello, ${p.name}, age ${p.age}`;
}

const e: Employee = { name: "Bob", age: 40, id: 999 };
const msg = greet(e);  // OK: Employee <: Person by width subtyping
  1. 5.3 Depth Subtyping Example
interface A { x: number }
interface B { x: 42 }     // literal type 42 <: number

let b: B = { x: 42 };
let a: A = b;            // OK: B <: A by depth subtyping
  1. 6 Operational Semantics

Extend evaluation with record and projection:

(E‐Rcd)
{li=vi}i=1nis a value \{\,l_i=v_i\}_{i=1}^n\; \text{is a value}

(E‐Proj)
$$ \infer { \{\,l_i=v_i\}_{i=1}^n . l_j \;\to\; v_j } {} $$

Plus congruence rules to evaluate subterms.

  1. 7 Type Safety with Subtyping

We state the usual theorems adapted to subsumption:

Theorem (Progress)
If ⌀⊢M:T\varnothing\vdash M:T then either MM is a value or there is M′M' with M→M′M\to M'.

Theorem (Preservation)
If Γ⊢M:T\Gamma\vdash M:T and M→M′M\to M' then Γ⊢M′:T\Gamma\vdash M':T.

The proofs proceed by induction on the typing derivation. The only non‐standard case is subsumption, which uses transitivity of <:<:.

  1. 8 Extensions and Remarks

– Bounded quantification (System F<:_{<:}) combines polymorphism with subtyping.
– Object types in OO languages generalize record subtyping with methods.
– Row‐polymorphism gives more flexible ways to talk about records with unknown extra fields.

  1. 9 Chapter Summary

– We added record types {li:τi}\{l_i:\tau_i\} and projection to the λ‐calculus.
– We defined a structural subtyping relation <:<:\, with reflexivity, transitivity, top, arrow subtyping, and three forms of record subtyping: width, depth, and permutation.
– We extended the typing rules with a subsumption rule to allow T<:ST<:S.
– We illustrated ideas in OCaml (by analogy) and TypeScript (structural subtyping).
– We showed that progress and preservation still hold.

In Chapter 7 we will introduce sum types, variant types, and pattern matching, further enriching our typed λ‐calculi.

Chapter 7 Parametric Polymorphism: System F and Beyond

In this chapter we explore parametric polymorphism, beginning with the second-order λ-calculus (System F), its syntax, typing and reduction rules, and demonstrating how it subsumes many ad hoc encodings of data. We then examine the theory of parametricity (Reynolds’s abstraction theorem and free theorems) and outline extensions such as existential types for data abstraction, bounded quantification (F<:), type operators (System Fω), and practical let-polymorphism as in ML and Haskell.

  1. 1 Overview of Polymorphism
    • Monomorphic vs. preorder polymorphism vs. parametric polymorphism
    • Importance: code reuse, data abstraction, generic libraries
    • Two main styles
    – Implicit “let-polymorphism” (Hindley–Milner)
    – Explicit second-order polymorphism (System F)

  2. 2 System F: Syntax and Operational Semantics

  3. 2.1 Types and Terms
    Types and terms of System F extend STLC with universal quantification: τ::=α∣τ1→τ2∣∀α.τ \tau ::= \alpha \mid \tau_1 \to \tau_2 \mid \forall\alpha.\,\tau M::=x∣λx:τ.M∣MN∣Λα.M∣M[τ] M ::= x \mid \lambda x\!:\!\tau.\,M \mid M\,N \mid \Lambda\alpha.\,M \mid M\,[\tau]

  4. 2.2 Reduction Rules
    • β-reduction (term‐level):
    (λx:τ.M)N→βM[N/x].(\lambda x\!:\!\tau.\,M)\,N \;\to_\beta\; M[N/x].
    • β₂-reduction (type‐level):
    (Λα.M)[τ]→β2M[τ/α].(\Lambda\alpha.\,M)\,[\tau] \;\to_{\beta2}\; M[\tau/\alpha].
    • Congruence rules to reduce inside applications and type-applications.
    We write →*\to^* for the reflexive–transitive closure of →β∪→β2\to_\beta\cup\to_{\beta2}.

  5. 3 Typing System F

  6. 3.1 Contexts and Judgments
    A typing context Γ\Gamma maps term variables to types and contains type-variable declarations. We write Γ⊢M:τ \Gamma \vdash M:\tau meaning under type‐variables in Γ\Gamma and term‐bindings in Γ\Gamma, term MM has type τ\tau.

  7. 3.2 Inference Rules

(Var)
$$ \infer{\Gamma,x:\tau\vdash x:\tau}{} $$

(Abs)
$$ \infer{\Gamma\vdash \lambda x\!:\!\tau_1.\,M : \tau_1\to\tau_2} {\Gamma,x:\tau_1\vdash M:\tau_2} $$

(App)
$$ \infer{\Gamma\vdash M\,N:\tau_2} {\Gamma\vdash M:\tau_1\to\tau_2 \quad \Gamma\vdash N:\tau_1} $$

(Gen)
$$ \infer{\Gamma\vdash \Lambda\alpha.\,M : \forall\alpha.\,\tau} {\Gamma,\alpha\text{ type}\vdash M:\tau} $$

(Inst)
$$ \infer{\Gamma\vdash M\,[\sigma] : \tau[\sigma/\alpha]} {\Gamma\vdash M : \forall\alpha.\,\tau} $$

  1. 3.3 Examples of System F Terms
    • Polymorphic identity:
    𝗂𝖽=Λα.λx:α.x\mathsf{id} = \Lambda\alpha.\,\lambda x\!:\!\alpha.\,x
    Has type ∀α.α→α\forall\alpha.\,\alpha\to\alpha.
    • Polymorphic composition:
    𝖼𝗈𝗆𝗉𝗈𝗌𝖾=Λα.Λβ.Λγ.λf:(β→γ).λg:(α→β).λx:α.f(gx)\mathsf{compose} = \Lambda\alpha.\Lambda\beta.\Lambda\gamma.\, \lambda f:(\beta\to\gamma).\, \lambda g:(\alpha\to\beta).\, \lambda x:\alpha.\,f\,(g\,x)
    Has type ∀αβγ.(β→γ)→(α→β)→(α→γ)\forall\alpha\beta\gamma.\,(\beta\to\gamma)\to(\alpha\to\beta)\to(\alpha\to\gamma).

  2. 4 Encoding Data in System F

  3. 4.1 Church Booleans and Conditionals
    𝖡𝗈𝗈𝗅=∀α.α→α→α \mathsf{Bool} = \forall\alpha.\,\alpha\to\alpha\to\alpha 𝗍𝗋𝗎𝖾=Λα.λt:α.λf:α.t \mathsf{true} = \Lambda\alpha.\,\lambda t:\alpha.\,\lambda f:\alpha.\,t 𝖿𝖺𝗅𝗌𝖾=Λα.λt:α.λf:α.f \mathsf{false} = \Lambda\alpha.\,\lambda t:\alpha.\,\lambda f:\alpha.\,f

  4. 4.2 Church Numerals
    𝖭𝖺𝗍=∀α.(α→α)→α→α \mathsf{Nat} = \forall\alpha.\,(\alpha\to\alpha)\to\alpha\to\alpha n¯=Λα.λs:(α→α).λz:α.snz \overline{n} = \Lambda\alpha.\,\lambda s:(\alpha\to\alpha).\, \lambda z:\alpha.\,s^n\,z

  5. 4.3 Pairs, Sums, Lists
    Defined similarly via universal types: • 𝖯𝖺𝗂𝗋AB=∀α.(A→B→α)→α\mathsf{Pair}\,A\,B = \forall\alpha.\,(A\to B\to\alpha)\to\alpha.
    • 𝖤𝗂𝗍𝗁𝖾𝗋AB=∀α.(A→α)→(B→α)→α\mathsf{Either}\,A\,B = \forall\alpha.\,(A\to\alpha)\to(B\to\alpha)\to\alpha.
    • 𝖫𝗂𝗌𝗍A=∀α.(A→α→α)→α→α\mathsf{List}\,A = \forall\alpha.\,(A\to\alpha\to\alpha)\to\alpha\to\alpha.

  6. 5 Metatheory: Type Safety and Strong Normalization

  7. 5.1 Type Safety
    • Preservation and Progress hold in System F by standard proofs (TAPL §15).
    • No stuck well‐typed terms, including type‐applications.

  8. 5.2 Strong Normalization
    • Every well‐typed term in System F is strongly normalizing (Girard’s theorem).
    • Consequence: no general recursion can be typed in System F.

  9. 6 Parametricity and Free Theorems

  10. 6.1 Reynolds’s Abstraction Theorem
    For each closed term M:τM:\tau there is a relational interpretation ⟦τ⟧\llbracket\tau\rrbracket such that (M,M)∈⟦τ⟧(M,M)\in \llbracket\tau\rrbracket. This yields “theorems for free” about the behavior of MM under all instantiations.

  11. 6.2 Logical Relations
    • Define logical relations on types by induction:
    – Base types: arbitrary relations.
    – Arrow: RA→B(f,g)R_{A\to B}(f,g) iff ∀(x,y)∈RA.(fx,gy)∈RB\forall (x,y)\in R_A.\;(f\,x,g\,y)\in R_B.
    – Universal: R∀α.τ(M,N)R_{\forall\alpha.\,\tau}(M,N) iff for all relations SS, (M[A],N[B])∈Rτ[S/α](M\,[A],N\,[B])\in R_{\tau[S/\alpha]}.
    • The Fundamental Theorem: well‐typed terms preserve relations.

  12. 6.3 Examples of Free Theorems
    • Any f:∀α.[α]→[α]f:\forall\alpha.\,[\alpha]\to[\alpha] satisfies f[A]∘𝑚𝑎𝑝g=𝑚𝑎𝑝g∘f[B]f\,[A]\circ \mathit{map}_g = \mathit{map}_g\circ f\,[B].
    • Any h:∀α.α→α→αh:\forall\alpha.\,\alpha\to\alpha\to\alpha is associative and commutative up to extensional equality (if swapped arguments).

  13. 7 Beyond System F

  14. 7.1 Existential Types for Data Abstraction
    Extend types with ∃α.τ\exists\alpha.\,\tau and terms: 𝗉𝖺𝖼𝗄M𝖺𝗌∃α.τ,𝗎𝗇𝗉𝖺𝖼𝗄[α,x]=M𝗂𝗇N \mathsf{pack}\;M\;\mathsf{as}\;\exists\alpha.\,\tau, \quad \mathsf{unpack}\;[\alpha,x]=M\;\mathsf{in}\;N Typing and reduction follow TAPL §15.4.

  15. 7.2 Bounded Quantification (System F<:)
    Allow ∀α<:T.τ\forall\alpha<:T.\,\tau with instantiation restricted by a subtype bound. Typing rules and subtyping (see TP §22).

  16. 7.3 Type Operators and Kind‐Polymorphism (System Fω)
    Introduce types as first‐class:
    κ::=*∣κ1→κ2 \kappa ::= * \mid \kappa_1\to\kappa_2 T::=α∣T1T2∣λα:κ.T∣∀α:κ.T T ::= \alpha \mid T_1\;T_2 \mid \lambda\alpha:\kappa.\,T \mid \forall\alpha:\kappa.\,T Enables higher‐kind constructors (e.g., List, Maybe).

  17. 7.4 Recursive Types and Iso‐Recursion
    System Fωμ or μF combines Fω with fixed‐point operators on types. Supports truly generic data types.

  18. 8 Practical Let-Polymorphism

  19. 8.1 Hindley–Milner Revisited
    • Implicit quantification only at let-bindings.
    • Principal‐type inference via Algorithm W (Chapter 5).
    • ML and Haskell implement variants with type classes or kind‐polymorphism.

  20. 8.2 Value Restriction and Effects
    • In presence of mutable references, only syntactic values may be generalized (value restriction).

  21. 9 Implementations and Examples

  22. 9.1 Haskell GADT Encoding

{-# LANGUAGE RankNTypes #-}

+  - polymorphic identity
id :: forall a. a -> a
id x = x


+  - Church-encoded list
type ListF a = forall r. (a -> r -> r) -> r -> r
nil :: ListF a
nil = \cons nil -> nil
cons :: a -> ListF a -> ListF a
cons x xs = \consF nilF -> consF x (xs consF nilF)
  1. 9.2 OCaml First-Class Modules as Existentials
module type STACK = sig
  type t
  val empty : t
  val push  : int -> t -> t
  val pop   : t -> (int * t)
end

module IntStack : STACK = struct
  type t = int list
  let empty = []
  let push x s = x::s
  let pop = function
    | x::xs -> (x,xs) | [] -> failwith "empty"
end

let use_stack (module S : STACK) =
  let open S in
  let s = push 1 empty in
  pop s
  1. 10 Chapter Summary
    • System F formalizes explicit parametric polymorphism; typable terms enjoy preservation, progress, and strong normalization.
    • Many datatypes and operations are encoded uniformly via universal quantification.
    • Parametricity yields “free theorems” and justifies data abstraction.
    • Extensions (existentials, bounded quantification, higher‐kinds, type operators) enrich expressiveness toward practical languages.
    • Let-polymorphism, type inference, and module systems bring polymorphism to everyday programming in ML and Haskell.

In Chapter 8 we will introduce algebraic data types with pattern matching, inductive proofs via structural recursion, and the categorical notion of initial algebras.

Chapter 8 Dependent Types and the Calculus of Constructions

In this chapter we move beyond simple and second‐order polymorphic types to fully dependent types, where types may depend on terms. We present the dependent λ–calculus with Π‐types, introduce Σ‐types and identity types, and then build up to the Calculus of Constructions (CoC), which combines dependent types and polymorphism into a single framework. Throughout, we emphasize the propositions-as-types interpretation and the basis for proof assistants such as Coq.

  1. 1 Motivation for Dependent Types
    • Types indexed by values enable rich specifications:
    – Vectors of length nn (𝖵𝖾𝖼An\mathsf{Vec}\;A\;n) rule out out‐of‐bounds errors at the type level.
    – Matrices, balanced trees, or sorted lists carry invariants in their types.
    • Propositions‐as‐types: logical quantification and proofs become terms in a dependently typed language.
    • Dependent types unify data and propositions, computation and proof.

  2. 2 The Dependent λ-Calculus (λΠ)

  3. 2.1 Syntax of Terms and Types
    • Sorts: 𝖯𝗋𝗈𝗉\mathsf{Prop} (propositions) and 𝖳𝗒𝗉𝖾\mathsf{Type} (types of computational data).
    • Types (and terms) A,B,CA,B,C and terms M,NM,N:
    A,B::=x∣𝖯𝗋𝗈𝗉∣𝖳𝗒𝗉𝖾∣Πx:A.B∣λx:A.M∣MN A,B\;::=\; x \mid \mathsf{Prop} \mid \mathsf{Type} \mid \Pi x:A.\,B \mid \lambda x:A.\,M \mid M\;N • A dependent function (Π‐type) Πx:A.B(x)\Pi x:A.\,B(x) generalizes both arrow types (A→BA\to B when BB does not depend on xx) and universal quantification (∀x:A.B(x)\forall x:A.\,B(x) in logic).

  4. 2.2 Contexts and Judgments
    • Context Γ\Gamma is a sequence of bindings x:Ax:A and type‐variable or constant declarations.
    • Judgments:
    – Γ⊢A:s\Gamma \vdash A : s (type AA has sort s∈{𝖯𝗋𝗈𝗉,𝖳𝗒𝗉𝖾}s\in\{\mathsf{Prop},\mathsf{Type}\})
    – Γ⊢M:A\Gamma \vdash M : A (term MM has type AA)
    • Conversion rule allows using A≡BA\equiv B up to β‐conversion (and in CoC up to higher universes).

  5. 2.3 Typing Rules
    (Var)
    $$ \infer{\Gamma,x:A\vdash x:A}{} $$

(Prod)
$$ \infer{\Gamma\vdash \Pi x:A.\,B : s_2} {\Gamma\vdash A:s_1 \quad \Gamma,x:A\vdash B:s_2 \quad (s_1,s_2)\in\{(\mathsf{Prop},\mathsf{Prop}),(\mathsf{Prop},\mathsf{Type}),(\mathsf{Type},\mathsf{Type})\}} $$

(Abs)
$$ \infer{\Gamma\vdash \lambda x:A.\,M : \Pi x:A.\,B} {\Gamma\vdash A:s \quad \Gamma,x:A\vdash M:B} $$

(App)
$$ \infer{\Gamma\vdash M\,N : B[N/x]} {\Gamma\vdash M:\Pi x:A.\,B \quad \Gamma\vdash N:A} $$

(Sort)
$$ \infer{\Gamma\vdash \mathsf{Prop}:\mathsf{Type}}{} \quad \infer{\Gamma\vdash \mathsf{Type}:\mathsf{Type}}{} $$

(Convert)
$$ \infer{\Gamma\vdash M:B} {\Gamma\vdash M:A \quad \Gamma\vdash A\equiv B} $$

  1. 2.4 Reduction and Conversion
    • β‐Reduction: (λx:A.M)N→M[N/x](\lambda x:A.\,M)\,N \to M[N/x].
    • Conversion (≡\equiv) is the reflexive‐transitive closure of β‐reduction on types.

  2. 3 Extensions: Σ‐Types and Identity Types

  3. 3.1 Σ–Types (Dependent Pairs)
    • Syntax: Σx:A.B(x)\Sigma x:A.\,B(x) is the type of pairs (a,b)(a,b) with a:Aa:A and b:B(a)b:B(a).
    • Introduction: 𝗉𝖺𝗂𝗋MN:Σx:A.B(x)\mathsf{pair}\;M\;N : \Sigma x:A.\,B(x).
    • Elimination (recursion):
    𝖾𝗅𝗂𝗆Σ[x,y]Pzu\mathsf{elim}_{\Sigma}\;[x,y]\,P\;z\;u
    projects and feeds components to a continuation.

  4. 3.2 Identity Types (Equality)
    • Syntax: for any A:𝖳𝗒𝗉𝖾A:\mathsf{Type} and x,y:Ax,y:A, 𝖨𝖽A(x,y):𝖳𝗒𝗉𝖾\mathsf{Id}_A(x,y) : \mathsf{Type}.
    • Introduction: 𝗋𝖾𝖿𝗅A(x):𝖨𝖽A(x,x)\mathsf{refl}_A(x) : \mathsf{Id}_A(x,x).
    • Elimination (J–rule / path induction) gives transport of proofs across equalities.

  5. 4 The Calculus of Constructions (CoC)

  6. 4.1 Combining Polymorphism and Dependent Types
    • CoC adds a new sort 𝖳𝗒𝗉𝖾0\mathsf{Type}_0, and allows 𝖳𝗒𝗉𝖾i:𝖳𝗒𝗉𝖾i+1\mathsf{Type}_i : \mathsf{Type}_{i+1} for an infinite hierarchy of universes.
    • Polymorphic abstraction Λα:𝖳𝗒𝗉𝖾i.M\Lambda\alpha:\mathsf{Type}_i.\,M and application M[A]M\,[A] are treated as special cases of λ‐abstraction and application at the level of sorts.

  7. 4.2 Syntax and Rules of CoC
    • Sorts s::=𝖯𝗋𝗈𝗉∣𝖳𝗒𝗉𝖾0∣𝖳𝗒𝗉𝖾1∣⋯s ::= \mathsf{Prop} \mid \mathsf{Type}_0 \mid \mathsf{Type}_1\mid\cdots
    • Extended Prod rule allows (s1,s2)∈{(𝖯𝗋𝗈𝗉,𝖯𝗋𝗈𝗉),(𝖯𝗋𝗈𝗉,𝖳𝗒𝗉𝖾i),(𝖳𝗒𝗉𝖾i,𝖳𝗒𝗉𝖾i)}(s_1,s_2)\in\{(\mathsf{Prop},\mathsf{Prop}),(\mathsf{Prop},\mathsf{Type}_i),(\mathsf{Type}_i,\mathsf{Type}_i)\}, and polymorphic Prod (𝖳𝗒𝗉𝖾i,𝖳𝗒𝗉𝖾i+1)(\mathsf{Type}_i,\mathsf{Type}_{i+1}).
    • All other rules (Var, Abs, App, Convert) carry over.

  8. 4.3 Curry–Howard: Propositions as Types
    • Type Πx:A.B(x)\Pi x:A.\,B(x) corresponds to ∀x:A.B(x)\forall x:A.\,B(x).
    • Implication A→BA\to B is Π_:A.B\Pi\_:A.\,B.
    • Conjunctions and disjunctions emerge from Σ‐types and tagged sums.
    • Proof terms are inhabitants of proposition‐types.

  9. 5 Examples in CoC

  10. 5.1 Natural Numbers and Induction
    In CoC with an inductive extension (Calculus of Inductive Constructions, CIC) one declares

Inductive nat : Type_0 :=
  | O : nat
  | S : nat → nat.

and obtains the induction principle 𝗇𝖺𝗍_𝗋𝖾𝖼𝗍:ΠP:𝗇𝖺𝗍→𝖳𝗒𝗉𝖾.PO→(Πn:𝗇𝖺𝗍.Pn→P(Sn))→Πn.Pn. \mathsf{nat\_rect} : \Pi P:\mathsf{nat}\to\mathsf{Type}.\; P\,O \to (\Pi n:\mathsf{nat}.\,P\,n\to P\,(S\,n))\to \Pi n.\,P\,n.

  1. 5.2 Vectors Indexed by Length
Inductive vec (A:Type_0) : nat → Type_0 :=
  | vnil  : vec A O
  | vcons : Π n:nat, A → vec A n → vec A (S n).

This encoding guarantees that vhead applied only to non‐empty vectors:

Definition vhead {A n} (v: vec A (S n)) : A :=
  match v with vcons _ x _ _ => x end.
  1. 6 Metatheory of CoC

  2. 6.1 Subject Reduction and Confluence
    • Reduction is confluent; type assignments are preserved under β‐reduction.
    • Conversion uses β‐equivalence, and Convert rule ensures well‐typedness is stable.

  3. 6.2 Strong Normalization and Consistency
    • CoC is strongly normalizing: no infinite reduction sequences for well‐typed terms.
    • Consistency: there is no term of type 𝖯𝗋𝗈𝗉→𝖳𝗒𝗉𝖾0\mathsf{Prop} \to \mathsf{Type}_0 proving false propositions.

  4. 7 Dependent Pattern Matching and Inductive Families

  5. 7.1 General Inductive Types
    • Inductive families generalize Σ‐ and identity types to arbitrary constructors indexed by parameters and indices.
    • Eliminators (recursors and inductive predicates) derive from type‐theoretic principles.

  6. 7.2 Proof by Dependent Elimination
    • Pattern match on an inductive proof to derive new proofs.
    • Tactics in proof assistants orchestrate these eliminations automatically.

  7. 8 Practical Systems: Coq and Agda

  8. 8.1 Coq’s Calculus of Inductive Constructions
    • Universe polymorphism, modules, notations, coercions, proof tactics.
    • Kernel: small trusted core performing type‐checking and reduction.

  9. 8.2 Agda
    • Interactive programming with dependent types, mixfix operators, totality checking.
    • Emphasis on equational style and user-defined inductive families.

  10. 9 Chapter Summary
    • Dependent types generalize simple and polymorphic types: Π\Pi‐types serve both as function spaces and universal quantification.
    • Σ‐types capture dependent pairs and logical existential quantification; identity types represent propositional equality.
    • The Calculus of Constructions unifies polymorphism and dependency with a hierarchy of universes.
    • Curry–Howard identifies proofs with terms; induction and pattern matching arise from inductive definitions.
    • Coq and Agda implement these theories, enabling interactive proof development and dependently typed programming.

In Chapter 9 we will study inductive and coinductive types in depth, covering structural and coinductive recursion, observational type theory, and applications to streams and reactive systems.

Chapter 9 Curry–Howard Correspondence: Logic as Types

In this chapter we present the remarkable correspondence between proofs in (intuitionistic) logic and well‐typed terms in a λ‐calculus. Under the Curry–Howard isomorphism (also “propositions‐as‐types” or “formulas‐as‐types”),
• propositions ↔︎ types
• proofs ↔︎ programs
• proof normalization (cut–elimination) ↔︎ term reduction
We begin with propositional logic and the simply‐typed λ‐calculus with products, sums, unit and void, then extend to second‐order and dependent logics, sketch the categorical semantics, and conclude with classical extensions and applications to proof assistants.

  1. 1 Propositional Logic and λ‐Calculus

  2. 1.1 Syntax of Propositional Formulas
    $$ φ,\;ψ ::= P \mid φ\toψ \mid φ\landψ \mid φ\lorψ \mid ⊤ \mid ⊥ $$ where PP ranges over propositional atoms.

  3. 1.2 Natural‐Deduction Rules
    – Implication
    • (→\to‐I) From Γ,φ⊢ψ\Gamma,φ⊢ψ infer $\Gamma⊢φ\toψ$.
    • (→\to‐E) From $\Gamma⊢φ\toψ$ and Γ⊢φ\Gamma⊢φ infer Γ⊢ψ\Gamma⊢ψ.
    – Conjunction
    • (∧\land‐I) From Γ⊢φ\Gamma⊢φ and Γ⊢ψ\Gamma⊢ψ infer $\Gamma⊢φ\landψ$.
    • (∧\land‐E₁,₂) From $\Gamma⊢φ\landψ$ infer Γ⊢φ\Gamma⊢φ (or Γ⊢ψ\Gamma⊢ψ).
    – Disjunction
    • (∨\lor‐I₁,₂) From Γ⊢φ\Gamma⊢φ infer $\Gamma⊢φ\lorψ$ (or from Γ⊢ψ\Gamma⊢ψ).
    • (∨\lor‐E) From $\Gamma⊢φ\lorψ$, Γ,φ⊢χ\Gamma,φ⊢χ, and Γ,ψ⊢χ\Gamma,ψ⊢χ infer Γ⊢χ\Gamma⊢χ.
    – Truth and False
    • (⊤‐I) Γ⊢⊤\Gamma⊢⊤.
    • (⊥‐E) From Γ⊢⊥\Gamma⊢⊥ infer Γ⊢φ\Gamma⊢φ for any φφ.

  4. 1.3 Simply‐Typed λ‐Calculus with Sums and Products
    Types
    A,B::=α∣A→B∣A×B∣A+B∣1∣0 A,B ::= \alpha \mid A\to B \mid A\times B \mid A+B \mid 1 \mid 0 Terms
    M,N::=x∣λx:A.M∣MN∣(M,N)∣𝖿𝗌𝗍M∣𝗌𝗇𝖽M∣𝗂𝗇𝗅M∣𝗂𝗇𝗋M∣𝖼𝖺𝗌𝖾M𝗈𝖿{𝗂𝗇𝗅x→N;𝗂𝗇𝗋y→P}∣()∣𝖺𝖻𝗈𝗋𝗍M M,N ::= x \mid \lambda x:A.\,M \mid M\,N \mid (M,N)\mid \mathsf{fst}\,M\mid\mathsf{snd}\,M \mid \mathsf{inl}\,M\mid\mathsf{inr}\,M\mid \mathsf{case}\;M\;\mathsf{of}\;\{\mathsf{inl}\;x\to N;\,\mathsf{inr}\;y\to P\} \mid () \mid \mathsf{abort}\,M Typing rules mirror the natural‐deduction rules above.

  5. 2 The Isomorphism: Propositions ≃ Types

  6. 2.1 Correspondence Table
    $φ\toψ$ ↔︎ A→BA\to B
    $φ\landψ$ ↔︎ A×BA\times B
    $φ\lorψ$ ↔︎ A+BA+B
    • ⊤⊤ ↔︎ 11 (unit type, term ()())
    • ⊥⊥ ↔︎ 00 (empty type, no constructors)
    • proof rule ↔︎ typing rule

  7. 2.2 Example: Proof of $φ\toψ\toφ$
    Natural‐deduction derivation
    $$ \infer[\to\text{I}] {⊢φ\to(ψ\toφ)} { \infer[\to\text{I}] {φ⊢ψ\toφ} { \infer[\to\text{E}] {φ,ψ⊢φ} {φ⊢φ & φ,ψ⊢ψ} } } $$ Corresponding λ‐term
    $$ \lambda x:φ.\,\lambda y:ψ.\,x \quad:\quad φ\to(ψ\toφ) $$

  8. 3 Second‐Order Logic and System F

  9. 3.1 Universal Quantification
    Logic: ∀X.φ(X)\forall X.\,φ(X)
    Types: ∀α.A(α)\forall α.\,A(α)
    Terms: type‐abstraction $\Lambdaα.\,M$ and type‐application M[B]M\,[B].

  10. 3.2 Example: Polymorphic Identity
    – Logic: ⊢∀X.X→X\vdash∀X.\,X\to X.
    – System F:
    $$ \mathsf{id} = \Lambdaα.\,\lambda x:α.\,x :\;\forallα.\,α\toα $$

  11. 4 Predicate Logic and Dependent Types

  12. 4.1 Universal ∀ and Π‐types
    Logic: ∀x:A.P(x)\forall x:A.\,P(x)
    Types: Πx:A.P(x)\Pi x:A.\,P(x)

  13. 4.2 Existential ∃ and Σ‐types
    Logic: ∃x:A.P(x)\exists x:A.\,P(x)
    Types: Σx:A.P(x)\Sigma x:A.\,P(x)
    – witness term a:Aa:A and proof p:P(a)p:P(a) give (a,p):Σx.P(x)(a,p):\Sigma x.\,P(x).

  14. 5 Cut‐Elimination ↔︎ Normalization

  15. 5.1 Cut‐Elimination in Logic
    – Removing intermediate lemmas (“cuts”) yields a normal proof.

  16. 5.2 β‐Reduction in λ‐Calculus
    – β\beta‐reducing (λx.M)N(\lambda x.\,M)\,N corresponds to eliminating an implication‐cut.
    – Strong normalization ↔︎ every proof normalizes.

  17. 6 Categorical Semantics: Cartesian and Bicartesian Closed Categories

  18. 6.1 CCC for Implicational Logic
    – Objects = types/propositions, morphisms = proofs.
    – Exponential BAB^A interprets A→BA\to B.
    – Terminal object 11 interprets ⊤⊤.

  19. 6.2 Bicartesian Closed Categories
    – Add binary products for ∧\land and coproducts for ∨\lor, initial object 00 for ⊥⊥.
    – The Curry–Howard–Lambek correspondence.

  20. 7 Classical Logic and Control Operators

  21. 7.1 Classical Axioms
    – Law of excluded middle (φ∨¬φφ\lor¬φ), double‐negation elimination ($¬¬φ\toφ$).

  22. 7.2 Control Constructs in Programming
    – Continuation‐passing style (CPS) translation of classical proofs.
    – call/cc in Scheme, 𝖼𝖺𝗍𝖼𝗁/𝗍𝗁𝗋𝗈𝗐\mathsf{catch}/\mathsf{throw} as proof terms for ⊥⊥‐elimination.

  23. 8 Proof Assistants and Program Extraction

  24. 8.1 Coq and Agda
    – Proofs are terms in CIC; extraction erases proofs to yield executable code.

  25. 8.2 Examples in Coq

Lemma and_comm (A B : Prop) : A ∧ B → B ∧ A.
Proof. intro p. destruct p as [a b]. split; assumption. Qed.
(* Extracted term:
   fun (A B:Type) (p:A*B) =>
     match p with (a,b) => (b,a) end
*)
  1. 9 Chapter Summary

• The Curry–Howard correspondence identifies propositions with types and proofs with programs.
• Natural‐deduction rules for ∧, ∨, →, ⊤, ⊥ correspond exactly to typing rules for products, sums, functions, unit, and void.
• Second‐order and dependent logics align with System F and λΠΣ‐calculi.
• Cut‐elimination ↔︎ β‐normalization; strong normalization implies consistency.
• Bicartesian closed categories provide semantics for propositional logic.
• Classical logic admits control operators and CPS translations.
• Proof assistants exploit Curry–Howard to build certified programs and extract executable code.

In Chapter 10 we turn to inductive and coinductive types in depth, exploring structural and coinductive recursion, observational type theory, and applications to infinite data structures and reactive systems.

Chapter 10 Semantic Analysis and Type-Checking Algorithms

In this chapter we leave the world of paper‐and‐pen type systems and show how to turn declarative typing rules into concrete algorithms. We cover the classic ML-style inference algorithm W, constraint-based approaches, algorithmic subtyping, bidirectional checking, and practical considerations such as symbol tables, incremental checking, and error reporting. We illustrate with OCaml‐style code.

10.1 The Compiler Front-End: Overview
• Phases
– Lexing & parsing ⇒ Abstract syntax tree (AST)
– Name resolution & scope checking ⇒ annotated AST with unique identifiers
– Type-checking / type inference ⇒ AST decorated with principal types or errors
– Desugaring & intermediate representation ⇒ for code generation or optimization
• Goals of semantic analysis
– Soundness: accept only well‐typed programs (subject‐reduction, progress)
– Decidability: algorithms terminate on all inputs
– Principal typings: compute most general types where possible
– Good diagnostics: report minimal, actionable error messages

10.2 Name Resolution and Symbol Tables
• Environments Γ\Gamma map identifiers to
– Declarations: 𝗏𝖺𝗅x:σ\mathsf{val}\;x:\sigma, 𝖿𝗎𝗇f:(τ→τ′)\mathsf{fun}\;f:(\tau\to\tau'),
– Type‐constructors, modules, record‐fields, etc.
• Scoping rules
– Nesting of 𝗅𝖾𝗍\mathsf{let}, λ\lambda, 𝗆𝗈𝖽𝗎𝗅𝖾\mathsf{module} ⇒ push/pop environments
– Shadowing of names
• Implementation sketch (OCaml pseudo‐code)

type 'a env = (string * 'a) list

let lookup (x:string) (env:'a env) : 'a option =
  try Some (List.assoc x env) with Not_found -> None

let extend (x:string) (v:'a) (env:'a env) : 'a env =
  (x,v)::env

(* Example: resolve a variable *)
type ty_scheme = Forall of string list * Mono.t
type raw_expr = Var of string | ...
type annotated_expr = Var of int        (* De Bruijn or unique id *) | ...

let resolve_names (env:unit env) (e:raw_expr) : annotated_expr = ...

10.3 From Declarative to Algorithmic Typing
• Declarative rules (e.g. for STLC, Hindley–Milner) are not directly executable
• Two main patterns:
– Syntax‐directed checking: every judgment Γ⊢M:A\Gamma⊢ M : A corresponds to exactly one rule
– Constraint generation + solving: collect constraints Ai=BjA_i = B_j or Ai<:BjA_i<:B_j then solve them
• Bidirectional approach: mix checking and inference to support more features with local annotations

10.4 ML-Style Type Inference: Algorithm W

10.4.1 Preliminaries: Types and Schemes
• Monotypes
τ::=α∣τ→τ∣τ×τ∣1∣0… τ ::= α \mid τ→τ \mid τ×τ \mid 1 \mid 0\;\dots
• Polytypes (schemes)
σ::=τ∣∀α1…αn.τ σ ::= τ \mid ∀α_1…α_n.\,τ
• Environments Γ:x↦σ\Gamma : x ↦ σ
• Instantiation 𝑖𝑛𝑠𝑡(∀α¯.τ)=τ[α¯↦β¯]\,\mathit{inst}(∀\overline α.\,τ) = τ[\overline α ↦ \overline β]\, (fresh α’s)
• Generalization 𝑔𝑒𝑛(Γ,τ)=∀α∉𝑓𝑣(Γ).τ\,\mathit{gen}(\Gamma,τ)=∀α∉\mathit{fv}(\Gamma).\;τ\,

10.4.2 Unification
• Substitutions S:α↦τS:α↦τ extend homomorphically
• Occurs‐check: prevent α=F(…α…)α = F(…α…)
• Algorithm:

exception Unify_error

let rec unify (t1:Mono.t) (t2:Mono.t) : Subst.t =
  match (t1,t2) with
  | (Mono.Var α, t) | (t, Mono.Var α) ->
      if t = Mono.Var α then Subst.id
      else if occurs α t then raise Unify_error
      else Subst.singleton α t
  | (Mono.Arr(a1,a2), Mono.Arr(b1,b2)) ->
      let s1 = unify a1 b1 in
      let s2 = unify (Subst.apply s1 a2) (Subst.apply s1 b2) in
      Subst.compose s2 s1
  | (Mono.Pair(a,b), Mono.Pair(c,d)) ->
      let s1 = unify a c in
      let s2 = unify (Subst.apply s1 b) (Subst.apply s1 d) in
      Subst.compose s2 s1
  | (Mono.Unit, Mono.Unit) -> Subst.id
  | _ -> raise Unify_error

10.4.3 The W Algorithm

(* W : env -> expr -> subst * typ *)
let rec W (env:ty_scheme env) (e:expr) : Subst.t * Mono.t =
  match e with
  | Var x ->
      (match lookup x env with
       | None -> error "unbound variable"
       | Some σ ->
           let τ = instantiate σ in
           (Subst.id, τ))
  | Lam(x, e1) ->
      let α = Mono.fresh_var () in
      let env' = extend x (Mono.Var α |> Scheme.of_mono) env in
      let (s1, τ1) = W env' e1 in
      (s1, Mono.Arr (Subst.apply s1 (Mono.Var α), τ1))
  | App(e1,e2) ->
      let (s1, τ1) = W env e1 in
      let (s2, τ2) = W (Subst.apply_env s1 env) e2 in
      let α = Mono.fresh_var () in
      let s3 = unify (Subst.apply s2 τ1) (Mono.Arr(τ2, Mono.Var α)) in
      (Subst.compose s3 (Subst.compose s2 s1),
       Subst.apply s3 (Mono.Var α))
  | Let(x,e1,e2) ->
      let (s1, τ1) = W env e1 in
      let σ = generalize (Subst.apply_env s1 env) τ1 in
      let env' = extend x σ (Subst.apply_env s1 env) in
      let (s2, τ2) = W env' e2 in
      (Subst.compose s2 s1, τ2)
  (* plus pairs, sums, unit, etc. *)

• Principal Types: W always computes the most general type
• Complexity: roughly O(n×α(n))O(n×α(n)) per unification; linear in program size for ML

10.5 Constraint-Based Inference and Solving
• Generate a set of equations C={τi=τj}C = \{τ_i = τ_j\} or subtyping constraints τi<:τjτ_i<:τ_j
• Solve via unification or by specialized algorithms (e.g. worklists, graph algorithms)
• Advantage: decouples AST traversal from solver; easier to extend to advanced type features
• Example: record subtyping constraints
– $ {l_1:τ_1,…,l_n:τ_n} <: {l_{i_1}:σ_{i_1},…,l_{i_k}:σ_{i_k}} $
– translate to τij<:σijτ_{i_j} <: σ_{i_j} for each field

10.6 Algorithmic Subtyping for F<:
• Declarative rules for <:<: can be non‐syntax‐directed because of transitivity
• Algorithmic presentation uses worklist/queue of pending checks

let rec subtype alg_env (t1,t2) =
  let worklist = Queue.create () in
  Queue.push (t1,t2) worklist;
  let rec loop () =
    if Queue.is_empty worklist then true else
    let (a,b) = Queue.pop worklist in
    match (a,b) with
    | (TVar x, TVar y) when x=y -> loop ()
    | (Arrow(a1,a2), Arrow(b1,b2)) ->
        Queue.push (b1,a1) worklist;
        Queue.push (a2,b2) worklist;
        loop ()
    | (Record flds1, Record flds2) when fields_of flds2 ⊆ fields_of flds1 ->
        List.iter (fun (l,σ) ->
          let τ = List.assoc l flds1 in
          Queue.push (τ,σ) worklist) flds2;
        loop ()
    | (Top, _) -> loop ()
    | _ -> false
  in loop ()

10.7 Bidirectional Type Checking
• Split judgments into
– Synthesis Γ⊢M⇒τ\Gamma⊢ M ⇒ τ (infer type of MM)
– Checking Γ⊢M⇐τ\Gamma⊢ M ⇐ τ (verify MM has given ττ)
• Rules ensure at most one applicable per syntax form
• Well suited for systems with GADTs, dependent types, implicit arguments

10.8 Practical Considerations

10.8.1 Incremental and Modular Checking
• Separate compilation: interface files (.mli.mli) store signatures
• Caching of typed ASTs; re‐typechecking only changed modules

10.8.2 Error Reporting and Localization
• Track source positions in AST nodes
• On unification/subtyping failure, record the smallest unsatisfiable subset of constraints
• Heuristics: hole‐driven diagnostic, blame assignment in gradual typing

10.9 Putting It All Together: A Mini-OCaml Type Checker

10.9.1 AST Definitions

type mono =
  | TVar of string
  | TArrow of mono * mono
  | TInt
  | TBool
  | TUnit

type scheme = Forall of string list * mono

type expr =
  | Var of string
  | Int of int
  | Bool of bool
  | Lam of string * expr
  | App of expr * expr
  | Let of string * expr * expr
  | If of expr * expr * expr
  | Pair of expr * expr
  | Fst of expr
  | Snd of expr

10.9.2 Environment and Substitution

module Subst = struct
  type t = (string * mono) list
  let id = []
  let singleton x τ = [(x,τ)]
  let rec apply (s:t) (τ:mono) : mono =
    match τ with
    | TVar x -> (try List.assoc x s with Not_found -> τ)
    | TArrow(a,b) -> TArrow(apply s a, apply s b)
    | TInt | TBool | TUnit -> τ
  let apply_env s env = List.map (fun (x,sch) ->
    (x, instantiate_scheme s sch)) env
  let compose s1 s2 =
    List.map (fun (x,τ) -> (x, apply s1 τ)) s2 @ s1
end

10.9.3 Type Inference Driver

let infer (e:expr) : mono =
  let (s,τ) = W [] e in
  Subst.apply s τ

10.10 Chapter Summary
• Semantic analysis orchestrates name resolution, scope checking, and type validation.
• Algorithm W performs efficient principal-type inference for Hindley–Milner polymorphism.
• Constraint generation separates AST traversal from solving, easing extensions.
• Algorithmic subtyping uses worklists to implement declarative rules without non‐syntax‐directed transitivity.
• Bidirectional checking scales to advanced type systems with local annotations.
• Practical issues—incrementality, error reporting, modularity—are crucial for real compilers.

In Chapter 11 we will explore advanced module systems and separate compilation, including ML‐style functors, first-class modules, and the theory of parameterized signatures.

Chapter 11 Lattice Theory in Type Systems and Abstract Interpretation

In this chapter we introduce the algebraic foundations of lattices and their central role in both advanced type systems (e.g. intersection/union types, subtype lattices) and in static analysis via abstract interpretation. We develop the theory of partial orders, complete lattices, monotone maps, and fixpoints (Knaster–Tarski, Kleene). Then we present the abstract interpretation framework—Galois connections, soundness, widening/narrowing—and illustrate with concrete OCaml sketches of constant propagation and sign analysis.

11.1 Partial Orders and Lattices
11.1.1 Definitions
• A partial order is a set (L,≤)(L,≤) satisfying reflexivity, antisymmetry, transitivity.
• A meet‐semi-lattice has binary meet ⊓\sqcap: x⊓yx\sqcap y is greatest lower bound (glb).
• A join‐semi-lattice has binary join ⊔\sqcup: x⊔yx\sqcup y is least upper bound (lub).
• A lattice has both ⊓\sqcap and ⊔\sqcup.

11.1.2 Examples
• Powerset lattice (𝒫(S),⊆)(\mathcal P(S),⊆) with ⊓=∩\sqcap=∩, ⊔=∪\sqcup=∪, bottom ∅∅, top SS.
• Integer intervals ordered by inclusion: [a,b]≤[c,d][a,b] ≤ [c,d] iff c≤a∧b≤dc≤a∧b≤d.
• Subtype lattice of record types under width/height subtyping.

11.2 Complete Lattices and Fixpoints
11.2.1 Complete Lattice
• (L,≤)(L,≤) is complete if every subset X⊆LX⊆L has ⨆X\bigsqcup X and ⨅X\bigsqcap X.
• In particular, bottom ⊥=⨆∅\bot=\bigsqcup∅, top ⊤=⨅∅\top=\bigsqcap∅.

11.2.2 Knaster–Tarski Theorem
Theorem. If f:L→Lf:L→L is monotone on a complete lattice, then its set of fixpoints forms a complete lattice. In particular,
𝑙𝑓𝑝(f)=⨅{x∈L∣f(x)≤x},𝑔𝑓𝑝(f)=⨆{x∈L∣x≤f(x)}. \mathit{lfp}(f)=\bigsqcap\{\,x∈L\mid f(x)≤x\}\;,\quad \mathit{gfp}(f)=\bigsqcup\{\,x∈L\mid x≤f(x)\}.

11.3 Monotone Functions and Kleene’s Fixpoint Theorem
• A function f:L→Lf:L→L is monotone if x≤y⇒f(x)≤f(y)x≤y⇒f(x)≤f(y).
• If LL is a chain‐complete poset and ff is continuous (preserves lub of ω‐chains), then
𝑙𝑓𝑝(f)=⨆n=0∞fn(⊥). \mathit{lfp}(f)=\bigsqcup_{n=0}^\infty f^n(\bot). • This yields the standard iterative fixpoint algorithm.

11.4 Lattices in Type Systems
11.4.1 Intersection Types
• Types A∧BA∧B denote values at both AA and BB.
• (𝒯,≤)(\mathcal T,≤) with ≤≤ as subtype relation extended by A∧B≤AA∧B≤A, A∧B≤BA∧B≤B, and if C≤AC≤A and C≤BC≤B then C≤A∧BC≤A∧B.
• Meet ⊓\sqcap is ∧∧, join ⊔\sqcup is least common supertype (if exists).

11.4.2 Union Types
• A∨BA∨B denotes values at either AA or BB.
• Join ⊔\sqcup is ∨∨, meet is greatest common subtype (often approximate).

11.4.3 Subtype Lattices
• Many type systems form bounded lattices: top ⊤⊤ (dynamic or any), bottom ⊥⊥ (uninhabited).
• Fields‐record subtyping and arrow‐covariance/contravariance form partial orders with meets/joins.

11.5 Abstract Interpretation Framework
11.5.1 Galois Connections and Insertions
A Galois connection between concrete domain (C,≤C)(C,≤_C) and abstract domain (A,≤A)(A,≤_A) is a pair (α:C→A,γ:A→C)(α:C→A,\,γ:A→C) such that
∀c∈C,∀a∈A.α(c)≤Aa⇔c≤Cγ(a). ∀c∈C,\;∀a∈A.\quad α(c)≤_A a \iff c≤_C γ(a).
If additionally α∘γ=𝑖𝑑Aα∘γ= \mathit{id}_A, it is a Galois insertion.

11.5.2 Abstraction and Concretization
• αα over‐approximates concrete sets; γγ maps abstract elements back to concrete semantics.
• To analyze a concrete semantic function f:C→Cf:C→C, define f♯=α∘f∘γ:A→Af^♯=α∘f∘γ : A→A.
• Soundness: α(f(c))≤Af♯(α(c))α(f(c)) ≤_A f^♯(α(c)) for all cc.

11.6 Common Abstract Domains
11.6.1 Sign Domain
Domain Asign={⊥,A_{sign}=\{\bot,\,Neg,Zero,Pos,NegZero,ZeroPos,NegPos,Top}\}.
Ordering by information: e.g. ⊥≤\bot≤Neg≤Top.
Join and meet tables are finite.

11.6.2 Interval Domain
Elements are intervals [a,b][a,b] with a∈ℤ∪{−∞}a∈\mathbb Z∪\{-∞\}, b∈ℤ∪{+∞}b∈\mathbb Z∪\{+∞\} and a≤ba≤b.
Join is convex hull, meet is intersection (or widened intersection).

11.6.3 Reduced Product
Combination of domains A×BA\times B quotiented to remove infeasible pairs, improving precision.

11.7 Fixpoint Computation Strategies
11.7.1 Kleene Iteration
Compute ascending chain ⊥,f(⊥),f2(⊥),…⊥,f(⊥),f^2(⊥),… until stabilization.

11.7.2 Worklist (Chaotic) Iteration
• Maintain a queue of program points whose abstract state may change.
• Recompute successors until no change.

11.7.3 Widening and Narrowing
• Widening ▽▽ forces chains to stabilize: xn+1=xn▽f(xn)x_{n+1}=x_n▽f(x_n).
• Narrowing △△ recovers precision: descending iteration starting from post‐widen fixpoint.

11.8 Implementation Sketch: A Mini Abstract Interpreter (OCaml)

11.8.1 Syntax of a Tiny Imp Language

type var = string
type expr =
  | Const of int
  | Var   of var
  | Add   of expr * expr
type stmt =
  | Assign of var * expr
  | Seq    of stmt * stmt
  | If     of expr * stmt * stmt
  | While  of expr * stmt

11.8.2 Sign Domain Representation

type sign = Bot | Neg | Zero | Pos | Top

let leq a b =
  match (a,b) with
  | (Bot,_) -> true
  | (_,Top) -> true
  | (Neg,Neg)|(Zero,Zero)|(Pos,Pos) -> true
  | _ -> false

let join a b =
  match (a,b) with
  | (Bot,x)|(x,Bot) -> x
  | (Neg,Pos)|(Pos,Neg) -> Top
  | (x,y) when x=y -> x
  | _ -> Top

let const_sign n =
  if n<0 then Neg else if n>0 then Pos else Zero

11.8.3 Abstract Evaluation

type abs_state = (var * sign) list

let lookup x st =
  try List.assoc x st with Not_found -> Bot

let eval_expr st = function
  | Const n  -> const_sign n
  | Var x    -> lookup x st
  | Add(e1,e2) ->
      join (eval_expr st e1) (eval_expr st e2)

11.8.4 Fixpoint Engine with Worklist and Widening

let analyze (prog:stmt) : abs_state =
  let states = Hashtbl.create 16 in
  let work = Queue.create () in
  (* init entry state *)
  Hashtbl.add states 0 []; Queue.push 0 work;
  while not (Queue.is_empty work) do
    let pc = Queue.pop work in
    let st = Hashtbl.find states pc in
    let st' = transfer prog st in
    let prev = Hashtbl.find states (succ pc) in
    let joined = join_states prev st' in
    let widened = if leq joined prev then prev else join prev joined in
    if not (leq widened prev) then begin
      Hashtbl.replace states (succ pc) widened;
      Queue.push (succ pc) work
    end
  done;
  Hashtbl.find states (final_pc prog)

11.9 Case Study: Constant Propagation
• Abstract domain: lattice of constants plus “unknown” ⊤\top.
• Transfer: propagate known constants through expressions; join at merges.
• Enables dead‐code elimination and strength reduction.

11.10 Case Study: Sign Analysis
• As above, tracks sign of integer variables.
• Widen intervals to ensure termination on loops.

11.11 Summary
• Lattices and complete lattices provide the algebraic backbone for both subtype/intersection‐union type systems and abstract domains in static analysis.
• Knaster–Tarski and Kleene fixpoint theorems guarantee existence and computability of fixpoints for monotone/continuous maps.
• Galois connections formalize sound abstractions; widening/narrowing enforce termination and precision trade‐offs.
• Worklist algorithms and OCaml sketches show how to build a minimal abstract interpreter for constant propagation and sign analysis.

In Chapter 12 we’ll explore effect systems and monadic semantics, showing how to track and control side effects in typed functional languages.

Chapter 12 Type Systems in Functional Programming Languages

In this chapter we survey the rich landscape of type‐system features as they appear in modern functional programming languages. We begin with the classic Hindley–Milner system of ML‐languages, extend it with algebraic data types and pattern matching, then move to Haskell’s powerful type‐class mechanism, GADTs, higher‐rank and higher‐kinded polymorphism, and ML‐style modules and functors. We conclude with a look at type‐level programming (type families, data kinds) and practical inference strategies and error reporting.

12.1 Hindley–Milner Polymorphism Revisited
12.1.1 Syntax and Declarative Typing
• Expressions
– Variables: xx
– Abstraction: λx.e\lambda x.\,e
– Application: e1e2e_1\,e_2
– Let‐binding: 𝗅𝖾𝗍x=e1𝗂𝗇e2\mathsf{let}\;x = e_1\;\mathsf{in}\;e_2
• Types
τ::=α∣τ1→τ2 \tau ::= α \mid τ_1 \to τ_2
• Typing rules Gen, Inst, Abs, App, Let (as in Chapter 10).
12.1.2 Principal Types and Inference (Algorithm W)
• Recall principal type property: every typable expression has a most general type σσ such that any other instance is an instantiation of σσ.
• Algorithm W from Section 10.4 infers principal types in O(nα(n))O(n\,α(n)) time.
12.1.3 Value Restriction in OCaml
• Mutable references break unrestricted generalization.
• OCaml enforces the value restriction: only syntactic values may be generalized in a 𝗅𝖾𝗍\mathsf{let}.

12.2 Algebraic Data Types and Pattern Matching
12.2.1 Definition of ADTs
• Sum‐of‐products types:
type ’a list = Nil | Cons of ’a * ’a list ```
• Constructors Ci:τi↦Dτ¯C_i : τ_i ↦ D\;\overline{τ}.
12.2.2 Typing Rules for Constructors and Case
– Constructor rule:
Γ⊢ei:τiΓ⊢C(e1,…,en):Dτ1⋯τn \Gamma⊢e_i:τ_i \quad \Gamma⊢C(e_1,\dots,e_n):D\;τ_1\cdots τ_n
– Case analysis:
$$ \infer { \Gamma⊢e:D\;\overline{τ} \quad\forall i.\;\Gamma,x_{i1}:τ_{i1},\dots,x_{ik_i}:τ_{ik_i}⊢e_i:τ } { \Gamma⊢\mathsf{match}\;e\;\mathsf{with}\;C_i(\overline{x_i})\to e_i : τ } $$
12.2.3 Type Inference for ADTs
• ADTs do not affect principal‐type inference: constructors are treated as polymorphic constants.
• Pattern‐match coverage and exhaustiveness checking ensure runtime safety.

12.3 Type Classes and Overloading (Haskell)
12.3.1 Class and Instance Declarations
• Class:
class Eq α where (==) :: α→α→Bool
• Instance:
instance Eq Int where x == y = x `intEq` y
12.3.2 Dictionary Passing Semantics
• Desugar each (==)(==)-call to passing a record of function pointers (the “dictionary”).
• At 𝖿x\mathsf{f}\;x with 𝖿::∀α.Eqα⇒α→α→Bool\mathsf{f} :: \forall α. Eq\;α ⇒ α→α→Bool, inference produces:
(d:𝐸𝑞𝐷𝑖𝑐𝑡τ),x:τ⊢d.(==)x (d:\mathit{EqDict}\;τ),\;x:τ⊢d.(==)\;x
12.3.3 Functional Dependencies and Multi‐Parameter Classes
• Allow declarations like
class Convertible α β | α → β where convert :: α→β
• FD α→βα→β enforces at most one ββ per αα.
12.3.4 Defaulting and coherence rules

12.4 Generalized Algebraic Data Types (GADTs)
12.4.1 Syntax
data Expr a where LitInt :: Int → Expr Int LitBool :: Bool → Expr Bool Add :: Expr Int → Expr Int → Expr Int If :: Expr Bool → Expr a → Expr a → Expr a
12.4.2 Typing
• Each constructor refines the result type aa in the context of the match.
• Typing rule for GADT‐case:
$$ \infer { \Gamma⊢e:Expr\,τ \quad\forall C_i.\;C_i:\overline{σ_i}→Expr\,τ_i \quad \Gamma,\overline{x_i:σ_i}⊢e_i:ρ } { Γ⊢\mathsf{case}\;e\;\mathsf{of}\;\dots\;C_i(\overline{x_i})→e_i : ρ } $$
12.4.3 Inference Challenges
• Type‐directed elaboration or bidirectional checking is required for principal typing.

12.5 Module Systems: ML‐Style Modules and Functors
12.5.1 Signatures
signature STACK = sig type 'a t val empty : 'a t val push : 'a → 'a t → 'a t val pop : 'a t → ('a * 'a t) option end
12.5.2 Structures and Functors
``` structure ListStack : STACK = struct type ’a t = ’a list let empty = [] let push x s = x::s let pop = function []→None | x::xs→Some(x,xs) end

functor MakeLoggingStack(X:STACK) = struct
  include X
  let push x s =
    print_endline "push";
    X.push x s
end
```  

12.5.3 Type Sharing and Abstraction
sharing type constraints enforce equal abstract types across signatures.
• Functor application type‐checks by signature matching and optionally volatile type generativity.

12.6 Advanced Polymorphism
12.6.1 Higher‐Kinded Types
• Kind system: *∣*→*∣(*→*)→**\mid *→* \mid (*→*)→* …
Monad :: (*→*)→* class Monad m where …
12.6.2 Rank‐N Types
• Haskell syntax:
f :: (∀α. α→α) → Int
• Requires explicit type annotations and bidirectional checking.
12.6.3 Row Polymorphism (Extensible Records/Variants)
• Types of the form { l_i:τ_i | ρ } with row variable ρρ.
• Enables polymorphic functions over records that may have extra fields.

12.7 Type‐Level Programming and Dependent‐Lite Features
12.7.1 Data Kinds and Kind Promotion
data Nat = Z | S Nat data Vec :: Nat → * → * where VNil :: Vec Z a VCons :: a → Vec n a → Vec (S n) a
12.7.2 Type Families and Functional Dependencies
type family F a b :: * type instance F Int Bool = Char
12.7.3 Implicit Arguments and Dependent Patterns (Agda/Idris style)

12.8 Implementation and Inference in Practice
12.8.1 Bidirectional Type Checking
• Split judgments: inference () vs. checking ().
• Ensures decidability in presence of higher‐rank and GADTs.
12.8.2 Constraint Solving and Solver Plugins
• Class constraints solved by dictionary‐generation.
• GHC plugins may customize unification and constraint rewriting.
12.8.3 Error Reporting and Type Debugging
• Source‐span tracking for better diagnostics.
• Techniques: hole‐driven refinement, deferred constraint collection.

12.9 Chapter Summary
• ML‐style Hindley–Milner gives principled let‐polymorphism with efficient inference.
• Algebraic data types and pattern matching introduce sum‐and‐product types naturally.
• Haskell’s type classes provide ad‐hoc polymorphism via dictionary passing.
• GADTs and higher‐rank polymorphism push the boundary toward dependent types and require bidirectional checking.
• Module systems support large‐scale program structuring with abstract types and parameterization.
• Advanced features—higher kinds, row polymorphism, type‐level programming—enable expressive, correct-by‐construction code.
• In practice, bidirectional checking, constraint solving, and improved error messages make sophisticated type systems tractable in real compilers.

Chapter 13 Type Systems in Object-Oriented, Logic-Oriented & Imperative Languages

In this chapter we survey how typing disciplines adapt to non-functional paradigms: class-based object-oriented languages, logic programming languages, and imperative languages with mutable state. We study their core calculi, typing rules, inference challenges, subtyping, generics, effects, ownership, and typestate.

13.1 Object-Oriented Type Systems
13.1.1 Core Calculus: F<: and Featherweight Java
• Syntax (classes, fields, methods):
L ::= C extends D { f; K; M }
K ::= C(x f) { super(x); this.f = x; }
M ::= T m(T x){ return e; }
e ::= x | e.f | e.m(e) | new C(e) | (T) e
```
• Nominal subtyping: C<:DC<:D iff CC extends DD (transitively).
• Typing rules (select, invoke, new, cast) mirror STLC but with subtyping:
– T-NEW: if fields(C)=Tififields(C) = T_i\,f_i and Γ⊢ei:Ui\Gamma⊢e_i:U_i with Ui<:TiU_i<:T_i, then
Γ⊢𝗇𝖾𝗐C(ei):C\Gamma⊢\mathsf{new}\;C(e_i):C.
– T-INVK: if Γ⊢e0:C0\Gamma⊢e_0:C_0 and mtype(m,C0)=T¯→Tmtype(m,C_0) = \overline{T}\to T, and Γ⊢ei:Ui\Gamma⊢e_i:U_i with Ui<:TiU_i<:T_i, then
Γ⊢e0.m(e):T\Gamma⊢e_0.m(e):T.
– T-SUB and T-CAST handle explicit upcasts/downcasts.
• Safety: Preservation + Progress hold in Featherweight Java.

13.1.2 Generics and Variance (Java/C#)
• Generic class declaration: class C<X extends B> { … }.
• Erasure vs. reification: Java erases type parameters at runtime; C# reifies.
• Variance annotations:
– Java wildcards: List<? extends T> (covariant), List<? super T> (contravariant).
– C# out T (covariant), in T (contravariant).
• Sound typing: subtyping on parameterized types follows declaration-site variance or use-site wildcards.
• Example:
interface ReadOnlyList<out T> { T get(int i); } ReadOnlyList<Animal> animals = new ReadOnlyList<Dog>();

13.1.3 Path-Dependent and Structural Generics (Scala)
• Path-dependent types:
trait Container { type Elem; def get: Elem } def head(c: Container): c.Elem = c.get
• Structural typing via refinement:
type Sized = { def size: Int } def f(x: Sized) = x.size
• F-bounded polymorphism: class C[X <: C[X]] enables self types.

13.1.4 Dependent Object Types (DOT)
• Merge nominal and dependent types: x.T is a type selection.
• Syntax:
p ::= x | p.l T ::= { type L: S..U; val l: T } | p.L | T & T | T ∨ T | Top | Bot
• Typing judgments track refinement and recursive object types.
• Used to model Scala’s full type system.

13.2 Logic-Oriented Type Systems
13.2.1 Untyped vs. Typed Logic Programming
• Prolog has no static types; errors surface at runtime.
• Typed logic languages: Mercury, Gödel, λProlog introduce static type checking.
• Simple Hindley–Milner style for Horn clauses:
Clause : A ← B₁,…,B_n Atom : p(t₁,…,t_k)
with declared types for predicates p : τ₁×…×τ_k → Bool.
• Type‐checking ensures each p(t₁,…,t_k) has ti:τit_i:τ_i.

13.2.2 Mode and Determinism Typing (Mercury)
• Modes (in, out) annotate argument usage: :- mode foo(in, out) is det.
• Compiler checks that in arguments are ground on call and out are fresh on return.
• Determinism categories (det, semidet, multi, nondet) form a lattice to track success count.

13.2.3 Higher-Order and λProlog
• λProlog extends Horn clauses with simply‐typed λ-terms and higher-order unification.
• Types: τ::=α∣τ→ττ ::= α \mid τ→τ.
• Clauses and goals typed under this calculus; allows predicates as first‐class values.

13.2.4 Constraint Logic Programming (CLP)
• CLP(DD) integrates a domain DD (e.g. finite‐domains, reals) as an abstract lattice.
• Types and constraints align: unification becomes constraint solving in DD.
• Soundness: typed CLP ensures all constraint calls respect domain operation signatures.

13.3 Imperative Language Type Systems
13.3.1 STLC with References
• Extend STLC with mutable cells:
τ::=…∣𝖱𝖾𝖿τ τ ::= … \mid \mathsf{Ref}\;τ
e::=…∣𝗋𝖾𝖿e∣!e∣e:=e e ::= … \mid \mathsf{ref}\;e \mid !e \mid e:=e
• Typing rules:
– T-REF: if Γ⊢e:τ\Gamma⊢e:τ then Γ⊢𝗋𝖾𝖿e:𝖱𝖾𝖿τ\Gamma⊢\mathsf{ref}\;e:\mathsf{Ref}\;τ.
– T-DEREF: if Γ⊢e:𝖱𝖾𝖿τ\Gamma⊢e:\mathsf{Ref}\;τ then Γ⊢!e:τ\Gamma⊢!e:τ.
– T-ASSIGN: if Γ⊢e1:𝖱𝖾𝖿τ\Gamma⊢e_1:\mathsf{Ref}\;τ and Γ⊢e2:τ\Gamma⊢e_2:τ then $\Gamma⊢e_1:=e_2: \unit$.
• Store typing judgment Σ⊢μ\Sigma⊢\mu tracks heap cells.
• Preservation and Progress hold in the presence of a well‐typed store.

13.3.2 Effect Systems
• Annotate expressions with effect sets εε (e.g. {𝗋𝖾𝖺𝖽,𝗐𝗋𝗂𝗍𝖾}\{\mathsf{read},\mathsf{write}\}).
• Judgment: Γ⊢e:τ!ε\Gamma⊢ e : τ \;!\;ε.
• Compose effects:
$$ \infer {Γ⊢ e_1:τ_1\!ε_1 \quad Γ,x:τ_1⊢ e_2:τ_2\!ε_2} {Γ⊢\mathsf{let}\;x=e_1\;\mathsf{in}\;e_2 : τ_2\!ε_1∪ε_2} $$
• Enables static tracking of stateful operations, I/O, exceptions.

13.3.3 Region and Ownership Types
• Regions group allocated storage to enforce lifetimes and prevent dangling pointers.
• Ownership types (e.g. in Cyclone, Rust) track aliasing and mutation privileges:
– In Rust: &T (shared borrow), &mut T (unique borrow).
– Borrow checker enforces no mutable aliasing and lifetime scopes.
• Typestate (linear types) ensure protocols: e.g. file handles must be opened before read, closed after.

13.3.4 Mutable Object-Oriented Imperative Languages (Java/C)
• Java’s type system enforces memory safety (no pointer arithmetic), null‐checks deferred to runtime.
• C’s nominal types are unsound under casts and pointer arithmetic.
• Refinement types (Liquid Haskell, F*) add logical predicates τ{x∣p(x)}τ\,\{x\mid p(x)\} to guarantee array bounds, non-null, arithmetic invariants.

13.4 Combining Paradigms: Multiparadigm Languages
• Scala, F#, Kotlin integrate functional, OOP, imperative style under a unified type system (DOT in Scala, ML-style in F#).
• Hybrid features: async effects, algebraic effects, dependent typing fragments.

13.5 Chapter Summary
• Object-oriented type systems rest on nominal subtyping, generics, variance, and core calculi (F<:, Featherweight Java).
• Logic languages gain safety via simple Hindley–Milner typing, modes, determinism, and higher-order types.
• Imperative typing extends STLC with references, effects, regions, ownership, and refinement to ensure memory and protocol safety.
• Multiparadigm languages combine these techniques, showing the versatility of modern type‐system design.

Chapter 14 Type Inference, Gradual Typing and Future Directions

In this final chapter we survey the state of the art in automated and semi-automated typing techniques—how types are inferred, how static and dynamic typing can coexist, and which research directions lie ahead.

14.1 Foundations of Type Inference
14.1.1 Hindley–Milner and Algorithm W
• Recall types τ::=α∣τ1→τ2 τ ::= α \mid τ_1 → τ_2
and schemes σ::=∀α¯.τ. σ ::= ∀\,\overline{α}. τ.
• Algorithm W:
– Traverses the AST, generates fresh type variables αiα_i.
– Emits unification constraints τ1=τ2τ_1 = τ_2.
– Solves by union-find in nearly linear time O(nα(n))O(n α(n)).
• Principal typing: every expression has a most general type; instantiation yields all others.

14.1.2 Constraint-Based and Baader–Siek Variants
• Instead of immediate unification, collect constraints
C::=τ1≡τ2∣C∧C C ::= τ_1 ≡ τ_2 \mid C∧C
and solve in batch.
• Enables richer features (row polymorphism, type classes) by generalizing equality to other relations (e.g. ≤≤ for subtyping).
• Baader–Siek local inference: infer as much as possible, ask user for top-level annotations when needed.

14.1.3 Local vs. Global Inference
• ML–style inference is global within a module boundary: all definitions in scope influence each other.
• Local inference (e.g. Scala, Kotlin) requires user annotations at method boundaries; improves compile times and modularity.
• Trade-off: precision vs. predictability vs. performance.

14.2 Bidirectional Type Checking
• Split judgments:
– Synthesis (inference) Γ⊢e⇒τ\Gamma⊢ e ⇒ τ
– Checking (verification) Γ⊢e⇐τ\Gamma⊢ e ⇐ τ
• Rules direct the flow of information: annotations drive checking, while simple constructs synthesize.
• Essential for features that break principal typing:
– Higher-rank polymorphism (∀∀ inside arguments)
– GADTs (type‐refinement in pattern matches)
– Implicit/coercion resolution (type classes, implicits).

14.3 Inference for Advanced Type Features
14.3.1 GADTs and Existentials
• GADT constructors carry extra equalities:
data Expr a where LitInt :: Int → Expr Int If :: Expr Bool → Expr a → Expr a → Expr a ``• Unification must record and solve equalities $a ≡ Int$ in the branch whereLitInt` applies.
• Full inference is undecidable—bidirectional checking or local annotations are used.

14.3.2 Type Classes, Traits and Implicits
• Constraint generation: each usage of a class method emits a constraint EqτEq τ.
• Dictionary‐passing semantics at compile time; instance search reduces to satisfiability in a Horn‐clause database.
• Overlapping/undecidable instances require user annotations or pragmas.

14.3.3 Modular and Higher-Module Inference
• Functor and module systems obstruct classical inference: abstract types block unification across module boundaries.
• Solutions:
– Constraint packaging: carry module constraints in signatures.
– Partial inference inside modules; require user‐written signatures.

14.4 Gradual Typing
14.4.1 The Unknown Type and Consistency Relation
• Introduce the unknown type ? into the type grammar:
τ::=…∣? τ ::= … \mid \,?
• Consistency τ∼στ ∼ σ is a reflexive, symmetric relation that treats ? as consistent with every type.
• Static check: allow τ ∼ σ; dynamic semantics: insert casts to check at runtime.

14.4.2 Cast Insertion and Blame Calculus
• During elaboration, each implicit conversion between ττ and σσ (where τ∼στ ∼ σ) is translated to
⟨σ ⇐ τ⟩ e
a runtime check.
• Blame labels track which side (static or dynamic) is responsible for a type violation.
• Guarantees: “well-typed programs cannot be blamed on the static side.”

14.4.3 Implementations and Trade-Offs
• Racket: contracts as first-class, gradual by design.
• TypeScript/Dart/MyPy: gradual typing in mainstream JS/Python platforms; unsoundness accepted for interoperability.
• Performance: runtime casts incur overhead; optimizing cast‐elimination is an active area.

14.5 Refinement and Liquid Type Inference
14.5.1 Refinement Types and Predicates
• Types of the form
{x:τ∣p(x)} \{\,x:τ\mid p(x)\}
where p(x)p(x) is a logical predicate (e.g. 0≤x<𝗅𝖾𝗇(𝑎𝑟𝑟)0 ≤ x < \mathsf{len}(\mathit{arr})).
• Verification reduces to checking predicate implications via SMT solvers.

14.5.2 Liquid Types
• Restrict refinements to conjunctions of qualifiers drawn from a finite template set.
• Inference:
– Generate logical Horn constraints p⇒qp⇒q from program semantics.
– Solve using predicate abstraction and fixpoint iteration.
• Systems: Liquid Haskell, F★, Dafny.

14.5.3 Precision vs. Automation
• More expressive predicates → more solver burden.
• User‐supplied “hints” or refinement annotations guide the inference.

14.6 Emerging Research Directions
• Effect Inference and Algebraic Effects
– Automatic inference of effect rows εε in τ→τ′!ετ→τ′ ! ε.
• Quantitative and Probabilistic Types
– Track resource usage, termination probabilities.
• Gradual Dependent Typing
– Marry full dependent types (ΠΠ‐types) with gradual casts and blame.
• Typed Meta-Programming and Macros
– Ensure hygiene and type safety in code generation (e.g. Typed Template Haskell).
• Cross-Language Type Interoperability
– Gradual and semantic types as a lingua franca for multi-language systems.

14.7 Challenges and Opportunities
• Better Error Reporting and Repair
– ML-based suggestions, interactive type debugging, root‐cause localization.
• Machine-Learning Guided Inference
– Predict missing annotations, select instances, guide solver strategies.
• Formalization and Mechanized Metatheory
– Scalable proof frameworks (e.g. Ott, Coq, Lean) for richer calculi.
• Towards Ubiquitous, Principled Typing
– Seamless blend of static/dynamic, modular, effectful, dependent features—bringing proof-power to mainstream programming.

14.8 Chapter Summary
• Type inference has evolved from pure HM/Algorithm W to constraint-based, bidirectional, and solver-aided approaches.
• Gradual typing offers a spectrum from static to dynamic checks, trading soundness for flexibility and interoperability.
• Refinement and liquid types push automated verification deeper, at the cost of heavier solver involvement.
• Future work lies in integrating effects, resources, and dependently typed abstractions in a gradual, user-friendly, and performant framework—bridging the gap between research proof systems and everyday software engineering.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment