A simple implementation of a call-by-value language with explicit parametric polymorphism Author: Frank Pfenning, Aug 2020 (v0.1) Oct 5, 2020 (v0.3) Oct 12, 2020 (v0.4) If binary is available (on linux.andrew.cmu.edu, at ~fp/bin) $ $bindir/lambda examples/comb.lam To build (requires mlton, see mlton.org) $ cd src $ make lambda $ ./lambda ../examples/comb.lam See examples/*.{lam,poly,cbv} for some simple examples To see options, invoke $ ./lambda -h All options can be given on the command line or at the beginning of the file. For example, $ ./lambda --abort=warning .cbv or in .cbv #options --abort=warning Tokens ====== ::= ' ' | \n | \t | \r | \v | \f ::= [a-zA-Z] ::= [_'] ::= [0-9] ::= ( | ) ( | | )* ::= ' [cbv0] ::= * Comments ======== single line: % ... \n nested delimited: (* ... *) Expressions and declarations ============================ Types (in order of precedence) tau1 tau2 left associative (prec. 4) * right associative (prec. 3) + right associative (prec. 2) -> right associative (prec. 1) !a. ?a. $a. 'i weak prefixes, binding a (max scope) (prec. 0) Terms (in order of precedence) e1 e2 e [tau] left associative, strongest precedence fold unfold 'i \x. $x. fst snd prefix , ; right associative, weakest precedence Tagged and untagged sums and products [cbv3] sum('i:tau_i) + sum('j:tau_i) is flattened to sum('k:tau_k) otherwise tau + sigma is parsed into ('l:tau) + ('r:sigma) prod('i:tau_i) & sum('j:tau_k) is flattened to prod('k:tau_k) otherwise tau & sigma is parsed into ('l:tau) & ('r:sigma) () stands for empty tagged sum or product sum() or prod() Type functions and applications can be used only for type definitions type d = \a1. ... \an. tau (defines d : type -> ... -> type) d tau1 ... taun (is a type) ::= '!' '.' % !a. tau | '->' % tau1 -> tau2 | '*' % tau1 * tau2 [cbv0] | '1' % 1 [cbv0] | '+' % tau1 + tau2 [cbv0] | '0' % 0 [cbv0] | : % 'i : tau [cbv1] | '&' % tau1 & tau2 [cbv3] | '(' ')' % () [cbv3] | '$' '.' % $a. tau [cbv0] | '?' '.' % ?a. tau [cbv2] | '\' '.' % \a. tau [cbv4] | % tau1 tau2 [cbv4] | '(' ')' % (tau) | % a ::= % x (variable) | '\' [':' ] '.' % \x. e (lambda abstraction) | % e1 e2 (application) | '(' ')' % (e) (scoping) | '/\' '.' % /\a. e (type abstraction) | '[' ']' % e [tau] (type application) | ',' % (e1,e2) (pair) [cbv0] | '(' ')' % () (unit) [cbv0] | % 'i e (tag) [cbv0/1] | '(|' '|)' % (| r |) (record) [cbv3] | '.' % e.'i (projection) [cbv3] | 'case' 'of' '(' [ ] ')' % case e of (brs) (case) [cbv0] | 'fold' % fold e (fold) [cbv0] | 'unfold' % unfold e (unfold) [cbv0] | '[' ']' ',' % ([tau], e) (pack) [cbv2] | '$' [ ':' ] '.' % $x. e (fix) [cbv0] ::= '=>' [ '|' ] % p1 => e1 | ... [cbv0] ::= '=>' [ '|' ] % 'i1 => e1 | ... [cbv3] ::= % x (variable) [cbv1] | ',' % (p1,p2) (pair) [cbv1] | '(' ')' % () (unit) [cbv1] | % 'i p (injection) [cbv1] | 'fold' % fold p (fold) [cbv1] | '[' ']' ',' % ([a],p) (pack) [cbv2] | '(' ')' ::= 'type' '=' % type a = tau | 'decl' ':' % decl x : tau | 'defn' '=' % defn x = e | 'norm' [ ] '=' % norm x = e --> defn x = norm(e) | 'conv' '=' % conv e1 = e2 verify norm(e1) = norm(e2) | 'fail' % fail succeeds if fails | 'eval' [ ] '=' % eval x = e --> defn x = eval(e) [cbv0] ::= '#' ... \n % for regression testing ::= * * Statics for --lang=cbv ====================== See lecture notes for Lecture 11 at http://www.cs.cmu.edu/~fp/courses/15814-f20/schedule.html and information below on --lang=poly Cases in the grammar above new for [cbv] are marked as such Variables in patterns or tags in sums may not be repeated Patterns in a case expression that are not exhaustive or redundant generate warnings. With --abort=warning (either on the command line or at the beginning of a .cbv file with #options --abort=warning) this will turn into an error. The bidirectional typechecker uses subtyping, unless disabled with --subtyping=false). Statics for --lang=poly ======================= Signature Sigma refers to all preceding successful declarations and definitions redefinition is not allowed Sigma ::= . | Sigma, a = tau | Sigma, x : tau type a = tau requires Sigma ; . |- tau type decl x : tau requires Sigma ; . |- tau type defn x = e requires Sigma ; . |- x : tau and Sigma ; . |- e <= tau or Sigma ; . |- e => tau for some tau norm x = e requires Sigma ; . |- x : tau and Sigma ; . |- e <= tau or Sigma ; . |- e => tau for some tau conv e1 = e2 requires Sigma ; . |- e1 => tau and Sigma ; . |- e2 => tau for some tau Typing ====== Sigma,Gamma ::= . | Gamma, a : type | Gamma, x : tau All type variables a distinct All expression variables x distinct Use alpha-conversion to maintain this presupposition =========================== Judgment Sigma |- Gamma ctx =========================== presupposes . |- Sigma ctx Sigma |- Gamma ctx ------------------------------ Sigma |- (Gamma, a : type) ctx Sigma |- Gamma ctx Sigma ; Gamma |- tau : type ----------------------------- Sigma |- (Gamma, x : tau) ctx ==================================== Judgment Sigma ; Gamma |- tau : type ==================================== presupposes . |- Sigma ctx and Sigma |- Gamma ctx a : type in Sigma a : type not in Gamma -------------------------- Sigma ; Gamma |- a : type a : type in Gamma ------------------------ Sigma ; Gamma |- a : type Sigma ; Gamma |- tau1 : type Sigma ; Gamma |- tau2 : type ------------------------------------- Sigma ; Gamma |- tau1 -> tau2 : type Sigma ; Gamma, a : type |- tau : type -------------------------------------- Sigma ; Gamma |- !a. tau : type ===================================== Judgments Sigma ; Gamma |- e => sigma (e synthesizes sigma) Sigma ; Gamma |- e <= tau (e checks against tau) ===================================== presuppose . |- Sigma ctx Sigma |- Gamma ctx Sigma ; Gamma |- tau : type ensure Sigma ; Gamma |- sigma : type x : tau in Sigma x : tau not in Gamma -------------------------- syn/def Sigma ; Gamma |- x => tau x : tau in Gamma -------------------------- syn/var Sigma ; Gamma |- x => tau Sigma ; Gamma |- e1 => tau2 -> tau1 Sigma ; Gamma |- e2 <= tau2 ------------------------------------ syn/app Sigma ; Gamma |- e1 e2 => tau1 [Sigma ; Gamma |- tau1' = tau1] Sigma ; Gamma, x1 : tau1 |- e2 <= tau2 -------------------------------------------------- chk/lam Sigma ; Gamma |- \x1 [:tau1']. e2 <= tau1 -> tau2 Sigma ; Gamma |- tau1 type Sigma ; Gamma |- e2 <= tau2 ------------------------------------------------ syn/lam Sigma ; Gamma |- \x1 : tau1. e2 => tau1 -> tau2 Sigma ; Gamma, a : type |- e <= tau ------------------------------------ chk/tplam Sigma ; Gamma |- /\a. e <= !a. tau Sigma ; Gamma, a : type |- e => tau ------------------------------------ syn/tplam Sigma ; Gamma |- /\a. e => !a. tau Sigma ; Gamma |- e => !a. sigma Sigma ; Gamma |- tau : type ---------------------------------------- syn/tpapp Sigma ; Gamma |- e[tau] => [tau/a]sigma Sigma ; Gamma |- e => tau' Sigma ; Gamma |- tau' = tau --------------------------- chk/syn Sigma ; Gamma |- e <= tau Examples ======== --lang=lam defn K = \x. \y. x defn S = \x. \y. \z. x z (y z) defn I = \x. x conv S K K = I % confirming associativity conv S = \x. \y. \z. (x z) (y z) % cannot omit the pair of parentheses fail conv S = \x. \y. \z. x z y z --lang=poly % with type synthesis type bool = !a. a -> a -> a defn true = /\a. \x:a. \y:a. x defn false = /\a. \x:a. \y:a. y % with type declarations type nat = !a. (a -> a) -> a -> a decl zero : nat decl succ : nat -> nat defn zero = /\a. \s. \z. z defn succ = \n. /\a. \s. \z. s (n [a] s z) decl plus : nat -> nat -> nat defn plus = \n. \k. n [nat] succ k decl times : nat -> nat -> nat defn times = \n. \k. n [nat] (plus k) zero type nat2 = !c. (nat -> nat -> c) -> c decl pair : nat -> nat -> nat2 defn pair = \x. \y. /\c. \p. p x y decl pred2 : nat -> nat2 defn pred2 = \n. n [nat2] (\p. p [nat2] (\x. \y. pair (succ x) x)) (pair zero zero) decl pred : nat -> nat defn pred = \n. pred2 n [nat] (\x. \y. y) Statics for --lang=lam ====================== Vars refers to all preceding successfully defined variables redefinition is not allowed Vars ::= . | Vars, x (x <> '_', all x distinct) type a = tau disallowed decl x : tau disallowed defn x = e requires Vars ; . |- e closed (unless x = '_') norm x = e requires Vars ; . |- e closed (unless x = '_') conv e1 = e2