diff --git a/demos/system-F-type/F.cppo.ml b/demos/system-F-type/F.cppo.ml index 14a7da43ac5dae13e895874c795f7dd4dbc6b1ed..62f269d8aa3f42fc92ba901b7dad700406adb1f1 100644 --- a/demos/system-F-type/F.cppo.ml +++ b/demos/system-F-type/F.cppo.ml @@ -1,10 +1,14 @@ open AlphaLib open Abstraction -#include "AlphaLibMacros.cppo.ml" (* In this demo, only type variables are handled via AlphaLib. Term variables are represented as strings. *) +(* Type variables. *) + +type tyvar = + Atom.t + (* Types. *) type ('fn, 'bn) typ = @@ -16,19 +20,19 @@ type ('fn, 'bn) typ = (* Term variables. *) -and var = (string[@opaque]) +and tevar = (string[@opaque]) (* Terms. *) and ('fn, 'bn) term = - | TeVar of var - | TeAbs of var * ('fn, 'bn) typ * ('fn, 'bn) term + | TeVar of tevar + | TeAbs of tevar * ('fn, 'bn) typ * ('fn, 'bn) term | TeApp of ('fn, 'bn) term * ('fn, 'bn) term - | TeLet of var * ('fn, 'bn) term * ('fn, 'bn) term + | TeLet of tevar * ('fn, 'bn) term * ('fn, 'bn) term | TeTyAbs of ('bn, ('fn, 'bn) term) abstraction | TeTyApp of ('fn, 'bn) term * ('fn, 'bn) typ | TePair of ('fn, 'bn) term * ('fn, 'bn) term - | Teroj of int * ('fn, 'bn) term + | TeProj of int * ('fn, 'bn) term (* Visitor generation. *) @@ -51,7 +55,7 @@ and ('fn, 'bn) term = ] -(* Operations based on visitors. *) +(* Type abbreviations. *) type raw_typ = (string, string) typ @@ -63,6 +67,10 @@ type raw_term = type nominal_term = (Atom.t, Atom.t) term +(* Operations based on visitors. *) + +#include "AlphaLibMacros.cppo.ml" + __FA FA(typ) FA(term) diff --git a/demos/system-F-type/FTypeChecker.ml b/demos/system-F-type/FTypeChecker.ml new file mode 100644 index 0000000000000000000000000000000000000000..1ec0f903e2fe36650f2fa9e1cd6c6791370d13e1 --- /dev/null +++ b/demos/system-F-type/FTypeChecker.ml @@ -0,0 +1,148 @@ +open AlphaLib +open F + +(* -------------------------------------------------------------------------- *) + +(* Type environments. *) + +module TermVar = + String + +module TermVarMap = + Map.Make(TermVar) + +type env = { + tevars: nominal_typ TermVarMap.t; + tyvars: Atom.Set.t +} + +let empty : env = + { tevars = TermVarMap.empty; tyvars = Atom.Set.empty } + +exception UnboundTermVariable of tevar + +let lookup (env : env) (x : tevar) : nominal_typ = + try + (* The free type variables of this type cannot be captured; see below. *) + TermVarMap.find x env.tevars + with Not_found -> + raise (UnboundTermVariable x) + +let extend_with_tevar (env : env) (x : tevar) (ty : nominal_typ) : env = + (* We maintain the invariant that the free type variables in the codomain + of [env.tevars] form a subset of [env.tyvars]. *) + assert (Atom.Set.subset (fa_typ ty) env.tyvars); + { env with tevars = TermVarMap.add x ty env.tevars } + +let extend_with_tyvar (env : env) (a : tyvar) : env = + (* We assume that type variables are globally unique in the term that we are + type-checking. Thus, the \Lambda-bound name [a] cannot be in the domain + of the environment (i.e., it cannot have been previously bound). Therefore, + by the above invariant, it cannot be in the codomain of the environment + either. This implies that it is safe to look up a type in the environment. *) + assert (not (Atom.Set.mem a env.tyvars)); + { env with tyvars = Atom.Set.add a env.tyvars } + +(* -------------------------------------------------------------------------- *) + +(* Destructors. *) + +let unfold ty = + assert (wf_typ ty); + match ty with + | TyMu (a, body) -> + (* No shadowing within [ty] implies [a # ba(body)]. *) + assert (not (Atom.Set.mem a (ba_typ body))); + (* The free names of [ty] are free in [body] too. + Strong well-formedness for [body] yields [fa(body) # ba(body)]. + Therefore, we have [fa(ty) # ba(body)]. *) + assert (Atom.Set.disjoint (fa_typ ty) (ba_typ body)); + (* By the above, the bound names of [body] are disjoint with the + domain and codomain of the substitution [ty/a]. *) + subst_typ_typ1 ty a body + | _ -> + assert false + +exception NotAnArrow of nominal_typ + +let rec as_arrow ty : nominal_typ * nominal_typ = + match ty with + | TyArrow (ty1, ty2) -> + ty1, ty2 + | TyMu _ -> + as_arrow (unfold ty) + | _ -> + raise (NotAnArrow ty) + +exception NotAProduct of nominal_typ + +let rec as_product ty : nominal_typ * nominal_typ = + match ty with + | TyProduct (ty1, ty2) -> + ty1, ty2 + | TyMu _ -> + as_product (unfold ty) + | _ -> + raise (NotAProduct ty) + +exception NotAForall of nominal_typ + +let rec as_forall ty : tyvar * nominal_typ = + match ty with + | TyForall (a, ty) -> + a, ty + | TyMu _ -> + as_forall (unfold ty) + | _ -> + raise (NotAForall ty) + +(* -------------------------------------------------------------------------- *) + +(* An equality test. *) + +(* TEMPORARY should unfold recursive types on the fly *) + +exception TypeMismatch of nominal_typ * nominal_typ + +let (--) ty1 ty2 = + if not (equiv_typ ty1 ty2) then + raise (TypeMismatch (ty1, ty2)) + +(* -------------------------------------------------------------------------- *) + +(* The type-checker. *) + +let rec typeof env (t : nominal_term) : nominal_typ = + match t with + | TeVar x -> + lookup env x + | TeAbs (x, ty1, t) -> + let ty2 = typeof (extend_with_tevar env x ty1) t in + TyArrow (ty1, ty2) + | TeApp (t, u) -> + let ty1, ty2 = as_arrow (typeof env t) in + typeof env u -- ty1; + ty2 + | TeLet (x, t, u) -> + let env = extend_with_tevar env x (typeof env t) in + typeof env u + | TeTyAbs (a, t) -> + TyForall (a, typeof (extend_with_tyvar env a) t) + | TeTyApp (t, ty2) -> + let a, ty1 = as_forall (typeof env t) in + (* We need ba(ty1) # [ty2/a] for this substitution to be safe. *) + (* We have ba(ty1) # a because the type a.ty1 is well-formed. Weak uniqueness, + also known as no-shadowing, suffices. *) + assert (not (Atom.Set.mem a (ba_typ ty1))); (* TEMPORARY problem: ba is too strong *) + (* We have ba(ty1) # fa(ty2) because fa(ty2) is a subset of dom(env), that is, + env.tyvars, and typeof has the postcondition ba(\result) # env.tyvars. *) + subst_typ_typ1 ty2 a ty1 + | TePair (t1, t2) -> + TyProduct (typeof env t1, typeof env t2) + | TeProj (i, t) -> + assert (i = 1 || i = 2); + let ty1, ty2 = as_product (typeof env t) in + if i = 1 then ty1 else ty2 + +let typeof = + typeof empty diff --git a/demos/system-F-type/Main.ml b/demos/system-F-type/Main.ml index 32bddb0e870fc12c19d14ed419a4abbd793fc25a..5ae0e6dc6304a9a23f45cb00b6b0a836ec1469a8 100644 --- a/demos/system-F-type/Main.ml +++ b/demos/system-F-type/Main.ml @@ -1 +1 @@ -module T = F +module T = FTypeChecker