This repository is meant to be a personal collection of implementations of the concepts from the TaPL(Types and Programming Languages) book.
(There are code samples available on TAPL's website(OCaml), but I chose to implement them from scratch on Haskell)
Each folder contains a parser+interpreter of a given type system. You just need to enter in the repository and cabal run tapl.
- Untyped lambda calculus(Chapter 5, 6, 7)
- Simply Typed Lambda Calculus(with extensions: records/tuples, variant/sum, etc.)(Chapter 9, 10, 11)
- Simply Typed Lambda Calculus with subtyping(Chapter 15, 16)
- Simply Typed Lambda Calculus with recursive types(Chapter 20, 21)
- Simply Typed Lambda Calculus with type reconstruction(type inference)(Chapter 22)
- System F(Chapter 23, 24, 25)
- System F with subtyping(Bounded Quantification)(Chapter 26, 28)
- System F omega(with type operators)(Chapter 29, 30)
The main type systems features covered on TaPL are:
- Types: allows you to classify the possible runtime values of terms, e.g. a variable of type
Nat, can only hold a natural number at runtime(e.g. 0, 1, 5, etc.). - Subtyping: allows you to have a more specific type than a more general one, i.e. given a type
S, which is a subtype of the typeT, all possible runtime values ofSwill be within all the possible runtime values ofT, e.g.Nat(0, 1, 2, ...) is a subtype ofInteger(-2, -1, 0, 1, 2, ...). We can use this feature on functions and variables, e.g. a function that needs aIntegeras an argument, can also take a variable of typeNatas an argument, becauseNatis a subtype ofInteger. - Recursive types: allows you to represent infinite data structures, e.g. a list of something(
data NatList = Nil | Cons Nat NatList), a tree of something, etc. - Type inference: allows you to omit the type annotations on your code, the compiler will infer the type of each term, e.g.
λn: succ (succ n)(instead ofλn:Nat. succ n)nwill be inferred to typeNat, because the compiler knows thatnmust be a natural number, since we're applying thesuccfunction on it, likewise withλb: if b then 0 else 2,bwill be inferred toBool, with no added type annotations. - Polymorphism(or more formally "Parametric polymorphism"): allows you to write generic functions(that operate on any type), e.g.
let id=(λT. λt:T. t), where the first argument is a type argument, and the second one is a term argument, with that we can use the same function on multiple types:id Nat 0will be evaluated to0,id Bool truewill be evaluated totrue, etc. - Higher-order polymorphism(or type operators): allows you to write generic functions on types, e.g. we can define the
Pairtype constructor(or kind), and we can feed this type constructor with two type arguments to yield a new type, e.g.Pair Nat Boolsymbolizes the type of a pair of a natural number and a boolean value.
| System | subtyping | recursive types | type inference | polymorphism(term expressions) | higher-order polymorphism(type expressions) |
|---|---|---|---|---|---|
| S.T.(Simply Typed) | - | - | - | - | - |
| S.T. w/ subtyping | X | - | - | - | - |
| S.T. w/ recursive | - | X | - | - | - |
| S.T. w/ reconstruction | - | - | X | - | - |
| System F | - | - | - | X | - |
| System F-sub | X | - | - | X | - |
| System F-omega | - | - | - | X | X |