Implementing Dependent Types in Haskell Andres Löh1 joint work with Conor McBride2 and Wouter Swierstra2 1 Universiteit 2 University Utrecht of Nottingham September 12, 2007 Motivation Present a type checker for dependent types, implemented in Haskell. Only a core language as a basis for experimentation: much like Fω is for Haskell/GHC. There are many design choices: Keep it simple . . . . . . yet powerful enough to demonstrate some of the advantages gained by dependent types. For programmers interested in type systems, not type theorists interested in programming. Andres Löh Implementing Dependent Types in Haskell 2 Why dependent types? Lots of type-level programming in Haskell: more static guarantees, but duplication of concepts on different layers more and more type system extensions some of them with restrictions and metatheory that is difficult to understand Andres Löh Implementing Dependent Types in Haskell 3 Why dependent types? Lots of type-level programming in Haskell: more static guarantees, but duplication of concepts on different layers more and more type system extensions some of them with restrictions and metatheory that is difficult to understand Dependent types offer: type-level programming becomes term-level programming programs, properties, and proofs within a single formalism a comparatively clean theory on the surface of course, there’s another set of problems, but . . . Andres Löh Implementing Dependent Types in Haskell 3 Simply-typed Lambda Calculus λ→ t ::= a | t → t0 Andres Löh Implementing Dependent Types in Haskell 4 Simply-typed Lambda Calculus λ→ t ::= a | t → t0 e ::= e :: t | x | e1 e2 | λx → e Andres Löh Implementing Dependent Types in Haskell 4 Simply-typed Lambda Calculus λ→ t ::= a | t → t0 e ::= e :: t | x | e1 e2 | λx → e Γ ::= ε | Γ, a :: ∗ | Γ, x :: t Andres Löh Implementing Dependent Types in Haskell 4 Simply-typed Lambda Calculus λ→ t ::= a | t → t0 e ::= e :: t | x | e1 e2 | λx → e Γ ::= ε | Γ, a :: ∗ | Γ, x :: t valid(Γ) valid(Γ) Γ ` t :: ∗ valid(ε) valid(Γ, a :: ∗) valid(Γ, x :: t) Andres Löh Implementing Dependent Types in Haskell 4 Simply-typed Lambda Calculus λ→ t ::= a | t → t0 e ::= e :: t | x | e1 e2 | λx → e Γ ::= ε | Γ, a :: ∗ | Γ, x :: t valid(Γ) valid(Γ) Γ ` t :: ∗ valid(ε) valid(Γ, a :: ∗) valid(Γ, x :: t) Γ(a) = ∗ Γ ` t :: ∗ Γ ` t0 :: ∗ Γ ` a :: ∗ Γ ` t → t0 :: ∗ Andres Löh Implementing Dependent Types in Haskell 4 Simply-typed Lambda Calculus λ→ t ::= a | t → t0 e ::= e :: t | x | e1 e2 | λx → e Γ ::= ε | Γ, a :: ∗ | Γ, x :: t valid(Γ) valid(Γ) Γ ` t :: ∗ valid(ε) valid(Γ, a :: ∗) valid(Γ, x :: t) Γ(a) = ∗ Γ ` t :: ∗ Γ ` t0 :: ∗ Γ ` a :: ∗ Γ ` t → t0 :: ∗ Γ ` t :: ∗ Γ ` e ::↓ t Γ ` (e :: t) ::↑ t Γ(x) = t Γ ` x ::↑ t Γ ` e ::↑ t Γ ` e ::↓ t Andres Löh Γ ` e1 ::↑ t → t0 Γ ` e2 ::↓ t Γ ` e1 e2 ::↑ t0 Γ, x :: t ` e ::↓ t0 Γ ` λx → e ::↓ t → t0 Implementing Dependent Types in Haskell 4 Evaluation in λ→ v ::= n | λx → v n ::= x | n v Andres Löh Implementing Dependent Types in Haskell 5 Evaluation in λ→ v ::= n | λx → v n ::= x | n v e⇓v e :: t ⇓ v e1 ⇓ λx → v1 e2 ⇓ v2 e1 e2 ⇓ v1 [x 7→ v2 ] Andres Löh x⇓x e1 ⇓ n1 e2 ⇓ v2 e1 e2 ⇓ n1 v2 e⇓v λx → e ⇓ λx → v Implementing Dependent Types in Haskell 5 Moving to dependent types: dependent functions The construct ∀x :: t.t0 (often also written Π x :: t.t0 ) generalizes and thereby replaces the function arrow t → t0 Difference: x may occur in t0 . If it doesn’t, we just write t → t0 as syntactic sugar. Note: This also generalizes (Haskell’s) parametric polymorphism, but we do not enforce parametricity. Andres Löh Implementing Dependent Types in Haskell 6 Moving to dependent types: everything is a term We collapse the multi-level structure (terms, types, [kinds]) – everything is a term. The ∀ moves to the term-level, in turn lambda abstraction and application become available to (former) types. The symbol :: becomes a relation between two terms. Computation arrives in the world of the types. Also automatically introduces “kinds”. Andres Löh Implementing Dependent Types in Haskell 7 Example We can state a large class of properties as types: ∀(a :: ∗) (xs :: List a).reverse (reverse xs) = = xs ∀(x :: Nat) (xs :: List Nat). Holds (sorted xs) → Holds (sorted (insert x xs)) An inhabitant of such types is a proof (Curry-Howard). Consistency of the type system is an advantage. Andres Löh Implementing Dependent Types in Haskell 8 Conversion rule Computation on types also introduces a problem: When are two types equal? Vec (2 + 2) Nat = Vec 4 Nat Vec (x + 0) Nat = Vec x Nat (assuming x :: Nat in the context) Vec (x + y) Nat = Vec (y + x) Nat (assuming x, y :: Nat in the context) Conversion rule: Γ ` e :: t0 t = t0 Γ ` e :: t In our case: evaluate both terms to normal form, then compare for (alpha-)equality. We try to keep the calculus strongly normalizing. Andres Löh Implementing Dependent Types in Haskell 9 Dependently-typed Lambda Calculus λΠ e, t ::= e :: t | ∗ | ∀x :: t.t0 | x | e1 e2 | λx → e Andres Löh Implementing Dependent Types in Haskell 10 Dependently-typed Lambda Calculus λΠ e, t ::= e :: t | ∗ | ∀x :: t.t0 | x | e1 e2 | λx → e Γ ::= ε | Γ, x :: t Andres Löh Implementing Dependent Types in Haskell 10 Dependently-typed Lambda Calculus λΠ e, t ::= e :: t | ∗ | ∀x :: t.t0 | x | e1 e2 | λx → e Γ ::= ε | Γ, x :: t valid(Γ) Γ ` t ::↓ ∗ valid(ε) valid(Γ, x :: t) Andres Löh Implementing Dependent Types in Haskell 10 Dependently-typed Lambda Calculus λΠ e, t ::= e :: t | ∗ | ∀x :: t.t0 | x | e1 e2 | λx → e Γ ::= ε | Γ, x :: t valid(Γ) Γ ` t ::↓ ∗ valid(ε) valid(Γ, x :: t) Γ(a) = ∗ Γ ` t :: ∗ Γ ` t0 :: ∗ Γ ` a :: ∗ Γ ` t → t0 :: ∗ Andres Löh Implementing Dependent Types in Haskell 10 Dependently-typed Lambda Calculus λΠ e, t ::= e :: t | ∗ | ∀x :: t.t0 | x | e1 e2 | λx → e Γ ::= ε | Γ, x :: t valid(Γ) Γ ` t ::↓ ∗ valid(ε) valid(Γ, x :: t) Γ(a) = ∗ Γ ` t :: ∗ Γ ` t0 :: ∗ Γ ` a :: ∗ Γ ` t → t0 :: ∗ Γ ` t ::↓ ∗ Γ ` e ::↓ t Γ ` (e :: t) ::↑ t Γ ` ∗ ::↑ ∗ Γ(x) = t Γ ` x ::↑ t Γ ` e1 ::↑ ∀x :: t.t0 Γ ` e2 ::↓ t Γ ` e1 e2 ::↑ t0 [x 7→ e2 ] Γ ` e ::↑ t0 t ⇓ v Γ ` e ::↓ t Andres Löh Γ ` t ::↓ ∗ Γ, x :: t ` t0 ::↓ ∗ Γ ` ∀x :: t.t0 ::↑ ∗ t0 ⇓ v Γ, x :: t ` e ::↓ t0 Γ ` λx → e ::↓ ∀x :: t.t0 Implementing Dependent Types in Haskell 10 Evaluation in λΠ v ::= x v | ∗ | ∀x :: v.v0 | λx → v Andres Löh Implementing Dependent Types in Haskell 11 Evaluation in λΠ v ::= x v | ∗ | ∀x :: v.v0 | λx → v e⇓v e :: t ⇓ v Andres Löh ∗⇓∗ t ⇓ v t0 ⇓ v 0 ∀x :: t.t0 ⇓ ∀x :: v.v0 x⇓x Implementing Dependent Types in Haskell 11 Evaluation in λΠ v ::= x v | ∗ | ∀x :: v.v0 | λx → v e⇓v e :: t ⇓ v ∗⇓∗ e1 ⇓ λx → v1 e2 ⇓ v2 e1 e2 ⇓ v1 [x 7→ v2 ] Andres Löh t ⇓ v t0 ⇓ v 0 ∀x :: t.t0 ⇓ ∀x :: v.v0 e1 ⇓ n1 e2 ⇓ v2 e1 e2 ⇓ n1 v2 x⇓x e⇓v λx → e ⇓ λx → v Implementing Dependent Types in Haskell 11 Implementation in Haskell Abstract Syntax Evaluation Substitution Typechecking Quotation Andres Löh Implementing Dependent Types in Haskell 12 Abstract Syntax data Term↑ = Ann Term↓ Term↓ | Star | Pi Term↓ Term↓ | Var Int | Par Name | Term↑ :@: Term↓ deriving (Show, Eq) data Term↓ = Inf Term↑ | Lam Term↓ deriving (Show, Eq) Andres Löh Implementing Dependent Types in Haskell 13 Contexts, Values type Type = Value type Context = [(Name, Type)] data Value = VLam (Value → Value) | VStar | VPi Value (Value → Value) | VNeutral Neutral data Neutral = NPar Name | NApp Neutral Value Andres Löh Implementing Dependent Types in Haskell 14 Evaluation eval↓ :: Term↓ → Env → Value eval↑ :: Term↑ → Env → Value Andres Löh Implementing Dependent Types in Haskell 15 Typechecking type↑ :: Int → Context → Term↑ → Result Type type↓ :: Int → Context → Term↓ → Type → Result () type↑ i Γ (Ann e t) = do type↓ i Γ t VStar let v = eval↓ t [ ] type↓ i Γ e v return v type↑ i Γ Star = return VStar Andres Löh Implementing Dependent Types in Haskell 16 Typechecking, continued type↑ :: Int → Context → Term↑ → Result Type type↓ :: Int → Context → Term↓ → Type → Result () type↑ i Γ (Pi t t0 ) = do type↓ i Γ t VStar let v = eval↓ t [ ] type↓ (i + 1) ((Bound i, v) : Γ) (subst↓ 0 (Par (Bound i)) t0 ) VStar return VStar subst↑ :: Int → Term↑ → Term↑ → Term↑ subst↓ :: Int → Term↑ → Term↓ → Term↓ Andres Löh Implementing Dependent Types in Haskell 17 Typechecking, continued type↓ i Γ (Inf e) v = do v0 ← type↑ i Γ e unless (quote0 v = = quote0 v0 ) (throwError "type mismatch") Andres Löh Implementing Dependent Types in Haskell 18 Typechecking, continued type↓ i Γ (Inf e) v = do v0 ← type↑ i Γ e unless (quote0 v = = quote0 v0 ) (throwError "type mismatch") quote0 :: Value → Term↓ quote0 = quote 0 quote :: Int → Value → Term↓ Andres Löh Implementing Dependent Types in Haskell 18 Typechecking, continued type↓ i Γ (Inf e) v = do v0 ← type↑ i Γ e unless (quote0 v = = quote0 v0 ) (throwError "type mismatch") quote0 :: Value → Term↓ quote0 = quote 0 quote :: Int → Value → Term↓ Example: quote 0 (VLam (λx → VLam (λy → x))) = Lam (quote 1 (VLam (λy → vpar (Unquoted 0)))) = Lam (Lam (quote 2 (vpar (Unquoted 0)))) = Lam (Lam (neutralQuote 2 (NPar (Unquoted 0)))) = Lam (Lam (Var 1)) Andres Löh Implementing Dependent Types in Haskell 18 Where are the dependent types? Total implementation is about 100 lines of Haskell code. Easy to see that we have gained advantages compared to λ→ . Hard to actually use the power of dependent types without adding datatypes to the language. Andres Löh Implementing Dependent Types in Haskell 19 Adding datatypes Add the type. Add the constructors (introduction forms). Types Add constructors to values. Add an eliminator (eliminator forms). Type Add evaluation rules for eliminator. Andres Löh Implementing Dependent Types in Haskell 20 Natural numbers e ::= · · · | Nat | Zero | Succ e | natElim e e e e v ::= · · · | Nat | Zero | Succ v n ::= · · · | natElim v v n Andres Löh Implementing Dependent Types in Haskell 21 Evaluation Nat ⇓ Nat Andres Löh Zero ⇓ Zero k⇓l Succ k ⇓ Succ l Implementing Dependent Types in Haskell 22 Evaluation Nat ⇓ Nat Zero ⇓ Zero mz ⇓ v natElim m mz ms Zero ⇓ v Andres Löh k⇓l Succ k ⇓ Succ l ms k (natElim m mz ms k) ⇓ v natElim m mz ms (Succ k) ⇓ v Implementing Dependent Types in Haskell 22 Typing Γ ` Nat :: ∗ Γ ` Zero :: Nat Andres Löh Γ ` k :: Nat Γ ` Succ k :: Nat Implementing Dependent Types in Haskell 23 Typing Γ ` Nat :: ∗ Γ ` Zero :: Nat Γ ` k :: Nat Γ ` Succ k :: Nat Γ ` m :: Nat → ∗ Γ, m :: Nat → ∗ ` mz :: m Zero Γ, m :: Nat → ∗ ` ms :: ∀k :: Nat.m k → m (Succ k) Γ ` n :: Nat Γ ` natElim m mz ms n :: m n Andres Löh Implementing Dependent Types in Haskell 23 Eliminator vs. fold natFold :: ∀m :: ∗ . natElim :: ∀m :: Nat → ∗. m → (m → m) → Nat → a m Zero → (∀k :: Nat.m k → m (Succ k)) → ∀n :: Nat.m n natFold r zero succ = natElim (λ → r) zero (λ rec → succ rec) Andres Löh Implementing Dependent Types in Haskell 24 Addition plus = natElim (λ → Nat → Nat) (λn → n) (λ rec n → Succ (rec n)) Andres Löh Implementing Dependent Types in Haskell 25 Implementation in Haskell Systematically extend all the functions . . . Andres Löh Implementing Dependent Types in Haskell 26 Vectors Vectors are lists that keep track of their length. Vec :: ∀(a :: ∗) (n :: Nat). ∗ Andres Löh Implementing Dependent Types in Haskell 27 Vectors Vectors are lists that keep track of their length. Vec :: ∀(a :: ∗) (n :: Nat). ∗ Nil :: ∀a :: ∗.Vec a Zero Cons :: ∀a :: ∗.∀n :: Nat.a → Vec a n → Vec a (Succ n) Andres Löh Implementing Dependent Types in Haskell 27 Vectors Vectors are lists that keep track of their length. Vec :: ∀(a :: ∗) (n :: Nat). ∗ Nil :: ∀a :: ∗.Vec a Zero Cons :: ∀a :: ∗.∀n :: Nat.a → Vec a n → Vec a (Succ n) vecElim :: ∀a :: ∗.∀m :: (∀n :: Nat.Vec a n → ∗). m Zero (Nil a) → (∀n :: Nat.∀x :: a.∀xs :: Vec a n. m n xs → m (Succ n) (Cons a n x xs)) → ∀n :: Nat.∀xs :: Vec a n.m n xs Andres Löh Implementing Dependent Types in Haskell 27 Vector append append = (λa → vecElim a (λm → ∀(n :: Nat).Vec a n → Vec a (plus m n)) (λ v → v) (λm v vs rec n w → Cons a (plus m n) v (rec n w))) :: ∀(a :: ∗) (m :: Nat) (v :: Vec a m) (n :: Nat) (w :: Vec a n). Vec a (plus m n) Andres Löh Implementing Dependent Types in Haskell 28 Other interesting types Zero One (or Unit) Two (or Bool) Fin Eq Holds dependent pairs ... Andres Löh Implementing Dependent Types in Haskell 29 Lots of missing features implicit arguments proper case analysis user feedback (error messages) ... Andres Löh Implementing Dependent Types in Haskell 30
© Copyright 2026 Paperzz