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) Sep 29, 2021 (v0.8) - added conv e1 = e2 to cbv language Oct 1, 2021 (v0.9) - allowed redundant branches when --subtyping=true Oct 25, 2021 (v1.0) - default is now no subtyping to get errors for incorrect code - redundant or extraneous patterns only give warning with --subtyping=true Nov 5, 2021 (v1.1) - added .prf extension for (hopefully sound) proof checking 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=prf ====================== Recursion at the level of types or expressions is disallowed, as are nonexhaustive or redundant pattern matches. Also, this option is incompatible with --subtyping=true Statics for --lang=cbv ====================== See rule sheet for Lecture 20 at http://www.cs.cmu.edu/~fp/courses/15814-f20/lectures/20-negatives-rules.pdf 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 --subtyping=true and errors with --subtyping=false. With --abort=warning (either on the command line or at the beginning of a .cbv file with #options --abort=warning) warnings will turn into an error. The bidirectional typechecker uses subtyping, unless disabled with --subtyping=false. conv e1 = e2 requires Sigma ; . |- e1 => tau and Sigma ; . |- e2 => tau and checks that if e1 |->* v1 and e2 |->* v2 then v1 = v2 where v1 and v2 may not contain values of negative type, that is, \x.e, /\a.e, or (| r |) 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 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