(* Title: Pure/envir.ML
Author: Lawrence C Paulson, Cambridge University Computer Laboratory
Free-form environments. The type of a term variable / sort of a type variable is
part of its name. The lookup function must apply type substitutions,
since they may change the identity of a variable.
*)
signature ENVIR =
sig
type tenv = (typ * term) Vartab.table
datatype env = Envir of {maxidx: int, tenv: tenv, tyenv: Type.tyenv}
val maxidx_of: env -> int
val term_env: env -> tenv
val type_env: env -> Type.tyenv
val is_empty: env -> bool
val empty: int -> env
val init: env
val merge: env * env -> env
val insert_sorts: env -> sort list -> sort list
val genvars: string -> env * typ list -> env * term list
val genvar: string -> env * typ -> env * term
val lookup1: tenv -> indexname * typ -> term option
val lookup: env -> indexname * typ -> term option
val update: (indexname * typ) * term -> env -> env
val above: env -> int -> bool
val vupdate: (indexname * typ) * term -> env -> env
val norm_type_same: Type.tyenv -> typ Same.operation
val norm_types_same: Type.tyenv -> typ list Same.operation
val norm_type: Type.tyenv -> typ -> typ
val norm_term_same: env -> term Same.operation
val norm_term: env -> term -> term
val beta_norm: term -> term
val head_norm: env -> term -> term
val eta_long: typ list -> term -> term
val eta_contract: term -> term
val beta_eta_contract: term -> term
val aeconv: term * term -> bool
val body_type: env -> typ -> typ
val binder_types: env -> typ -> typ list
val strip_type: env -> typ -> typ list * typ
val fastype: env -> typ list -> term -> typ
val subst_type_same: Type.tyenv -> typ Same.operation
val subst_term_types_same: Type.tyenv -> term Same.operation
val subst_term_same: Type.tyenv * tenv -> term Same.operation
val subst_type: Type.tyenv -> typ -> typ
val subst_term_types: Type.tyenv -> term -> term
val subst_term: Type.tyenv * tenv -> term -> term
val expand_atom: typ -> typ * term -> term
val expand_term: (term -> (typ * term) option) -> term -> term
val expand_term_frees: ((string * typ) * term) list -> term -> term
end;
structure Envir: ENVIR =
struct
(** datatype env **)
(*Updating can destroy environment in 2 ways!
(1) variables out of range
(2) circular assignments
*)
type tenv = (typ * term) Vartab.table;
datatype env = Envir of
{maxidx: int, (*upper bound of maximum index of vars*)
tenv: tenv, (*assignments to Vars*)
tyenv: Type.tyenv}; (*assignments to TVars*)
fun make_env (maxidx, tenv, tyenv) =
Envir {maxidx = maxidx, tenv = tenv, tyenv = tyenv};
fun maxidx_of (Envir {maxidx, ...}) = maxidx;
fun term_env (Envir {tenv, ...}) = tenv;
fun type_env (Envir {tyenv, ...}) = tyenv;
fun is_empty env =
Vartab.is_empty (term_env env) andalso
Vartab.is_empty (type_env env);
(* build env *)
fun empty maxidx = make_env (maxidx, Vartab.empty, Vartab.empty);
val init = empty ~1;
fun merge
(Envir {maxidx = maxidx1, tenv = tenv1, tyenv = tyenv1},
Envir {maxidx = maxidx2, tenv = tenv2, tyenv = tyenv2}) =
make_env (Int.max (maxidx1, maxidx2),
Vartab.merge (op =) (tenv1, tenv2),
Vartab.merge (op =) (tyenv1, tyenv2));
(*NB: type unification may invent new sorts*) (* FIXME tenv!? *)
val insert_sorts = Vartab.fold (fn (_, (_, T)) => Sorts.insert_typ T) o type_env;
(*Generate a list of distinct variables.
Increments index to make them distinct from ALL present variables. *)
fun genvars name (Envir {maxidx, tenv, tyenv}, Ts) : env * term list =
let
fun genvs (_, [] : typ list) : term list = []
| genvs (_, [T]) = [Var ((name, maxidx + 1), T)]
| genvs (n, T :: Ts) =
Var ((name ^ radixstring (26, "a" , n), maxidx + 1), T)
:: genvs (n + 1, Ts);
in (Envir {maxidx = maxidx + 1, tenv = tenv, tyenv = tyenv}, genvs (0, Ts)) end;
(*Generate a variable.*)
fun genvar name (env, T) : env * term =
let val (env', [v]) = genvars name (env, [T])
in (env', v) end;
fun var_clash xi T T' =
raise TYPE ("Variable has two distinct types", [], [Var (xi, T'), Var (xi, T)]);
fun lookup_check check tenv (xi, T) =
(case Vartab.lookup tenv xi of
NONE => NONE
| SOME (U, t) => if check (T, U) then SOME t else var_clash xi T U);
(*When dealing with environments produced by matching instead
of unification, there is no need to chase assigned TVars.
In this case, we can simply ignore the type substitution
and use = instead of eq_type.*)
fun lookup1 tenv = lookup_check (op =) tenv;
fun lookup (Envir {tenv, tyenv, ...}) = lookup_check (Type.unified tyenv) tenv;
fun update ((xi, T), t) (Envir {maxidx, tenv, tyenv}) =
Envir {maxidx = maxidx, tenv = Vartab.update_new (xi, (T, t)) tenv, tyenv = tyenv};
(*Determine if the least index updated exceeds lim*)
fun above (Envir {tenv, tyenv, ...}) lim =
(case Vartab.min tenv of SOME ((_, i), _) => i > lim | NONE => true) andalso
(case Vartab.min tyenv of SOME ((_, i), _) => i > lim | NONE => true);
(*Update, checking Var-Var assignments: try to suppress higher indexes*)
fun vupdate ((a, U), t) env =
(case t of
Var (nT as (name', T)) =>
if a = name' then env (*cycle!*)
else if Term_Ord.indexname_ord (a, name') = LESS then
(case lookup env nT of (*if already assigned, chase*)
NONE => update (nT, Var (a, T)) env
| SOME u => vupdate ((a, U), u) env)
else update ((a, U), t) env
| _ => update ((a, U), t) env);
(** beta normalization wrt. environment **)
(*Chases variables in env. Does not exploit sharing of variable bindings
Does not check types, so could loop.*)
local
fun norm_type0 tyenv : typ Same.operation =
let
fun norm (Type (a, Ts)) = Type (a, Same.map norm Ts)
| norm (TFree _) = raise Same.SAME
| norm (TVar v) =
(case Type.lookup tyenv v of
SOME U => Same.commit norm U
| NONE => raise Same.SAME);
in norm end;
fun norm_term1 tenv : term Same.operation =
let
fun norm (Var v) =
(case lookup1 tenv v of
SOME u => Same.commit norm u
| NONE => raise Same.SAME)
| norm (Abs (a, T, body)) = Abs (a, T, norm body)
| norm (Abs (_, _, body) $ t) = Same.commit norm (subst_bound (t, body))
| norm (f $ t) =
((case norm f of
Abs (_, _, body) => Same.commit norm (subst_bound (t, body))
| nf => nf $ Same.commit norm t)
handle Same.SAME => f $ norm t)
| norm _ = raise Same.SAME;
in norm end;
fun norm_term2 (envir as Envir {tyenv, ...}) : term Same.operation =
let
val normT = norm_type0 tyenv;
fun norm (Const (a, T)) = Const (a, normT T)
| norm (Free (a, T)) = Free (a, normT T)
| norm (Var (xi, T)) =
(case lookup envir (xi, T) of
SOME u => Same.commit norm u
| NONE => Var (xi, normT T))
| norm (Abs (a, T, body)) =
(Abs (a, normT T, Same.commit norm body)
handle Same.SAME => Abs (a, T, norm body))
| norm (Abs (_, _, body) $ t) = Same.commit norm (subst_bound (t, body))
| norm (f $ t) =
((case norm f of
Abs (_, _, body) => Same.commit norm (subst_bound (t, body))
| nf => nf $ Same.commit norm t)
handle Same.SAME => f $ norm t)
| norm _ = raise Same.SAME;
in norm end;
in
fun norm_type_same tyenv T =
if Vartab.is_empty tyenv then raise Same.SAME
else norm_type0 tyenv T;
fun norm_types_same tyenv Ts =
if Vartab.is_empty tyenv then raise Same.SAME
else Same.map (norm_type0 tyenv) Ts;
fun norm_type tyenv T = norm_type_same tyenv T handle Same.SAME => T;
fun norm_term_same (envir as Envir {tenv, tyenv, ...}) =
if Vartab.is_empty tyenv then norm_term1 tenv
else norm_term2 envir;
fun norm_term envir t = norm_term_same envir t handle Same.SAME => t;
fun beta_norm t =
if Term.could_beta_contract t then norm_term init t else t;
end;
(* head normal form for unification *)
fun head_norm env =
let
fun norm (Var v) =
(case lookup env v of
SOME u => head_norm env u
| NONE => raise Same.SAME)
| norm (Abs (a, T, body)) = Abs (a, T, norm body)
| norm (Abs (_, _, body) $ t) = Same.commit norm (subst_bound (t, body))
| norm (f $ t) =
(case norm f of
Abs (_, _, body) => Same.commit norm (subst_bound (t, body))
| nf => nf $ t)
| norm _ = raise Same.SAME;
in Same.commit norm end;
(* eta-long beta-normal form *)
fun eta_long Ts (Abs (s, T, t)) = Abs (s, T, eta_long (T :: Ts) t)
| eta_long Ts t =
(case strip_comb t of
(Abs _, _) => eta_long Ts (beta_norm t)
| (u, ts) =>
let
val Us = binder_types (fastype_of1 (Ts, t));
val i = length Us;
val long = eta_long (rev Us @ Ts);
in
fold_rev (Term.abs o pair "x") Us
(list_comb (incr_boundvars i u,
map (long o incr_boundvars i) ts @ map (long o Bound) (i - 1 downto 0)))
end);
(* full eta contraction *)
local
fun decr lev (Bound i) = if i >= lev then Bound (i - 1) else raise Same.SAME
| decr lev (Abs (a, T, body)) = Abs (a, T, decr (lev + 1) body)
| decr lev (t $ u) = (decr lev t $ decrh lev u handle Same.SAME => t $ decr lev u)
| decr _ _ = raise Same.SAME
and decrh lev t = (decr lev t handle Same.SAME => t);
fun eta (Abs (a, T, body)) =
((case eta body of
body' as (f $ Bound 0) =>
if Term.is_dependent f then Abs (a, T, body')
else decrh 0 f
| body' => Abs (a, T, body')) handle Same.SAME =>
(case body of
f $ Bound 0 =>
if Term.is_dependent f then raise Same.SAME
else decrh 0 f
| _ => raise Same.SAME))
| eta (t $ u) = (eta t $ Same.commit eta u handle Same.SAME => t $ eta u)
| eta _ = raise Same.SAME;
in
fun eta_contract t =
if Term.could_eta_contract t then Same.commit eta t else t;
end;
val beta_eta_contract = eta_contract o beta_norm;
fun aeconv (t, u) = t aconv u orelse eta_contract t aconv eta_contract u;
fun body_type env (Type ("fun", [_, T])) = body_type env T
| body_type env (T as TVar v) =
(case Type.lookup (type_env env) v of
NONE => T
| SOME T' => body_type env T')
| body_type _ T = T;
fun binder_types env (Type ("fun", [T, U])) = T :: binder_types env U
| binder_types env (TVar v) =
(case Type.lookup (type_env env) v of
NONE => []
| SOME T' => binder_types env T')
| binder_types _ _ = [];
fun strip_type env T = (binder_types env T, body_type env T);
(*finds type of term without checking that combinations are consistent
Ts holds types of bound variables*)
fun fastype (Envir {tyenv, ...}) =
let
val funerr = "fastype: expected function type";
fun fast Ts (f $ u) =
(case Type.devar tyenv (fast Ts f) of
Type ("fun", [_, T]) => T
| TVar _ => raise TERM (funerr, [f $ u])
| _ => raise TERM (funerr, [f $ u]))
| fast _ (Const (_, T)) = T
| fast _ (Free (_, T)) = T
| fast Ts (Bound i) =
(nth Ts i handle General.Subscript => raise TERM ("fastype: Bound", [Bound i]))
| fast _ (Var (_, T)) = T
| fast Ts (Abs (_, T, u)) = T --> fast (T :: Ts) u;
in fast end;
(** plain substitution -- without variable chasing **)
local
fun subst_type0 tyenv = Term_Subst.map_atypsT_same
(fn TVar v =>
(case Type.lookup tyenv v of
SOME U => U
| NONE => raise Same.SAME)
| _ => raise Same.SAME);
fun subst_term1 tenv = Term_Subst.map_aterms_same
(fn Var v =>
(case lookup1 tenv v of
SOME u => u
| NONE => raise Same.SAME)
| _ => raise Same.SAME);
fun subst_term2 tenv tyenv : term Same.operation =
let
val substT = subst_type0 tyenv;
fun subst (Const (a, T)) = Const (a, substT T)
| subst (Free (a, T)) = Free (a, substT T)
| subst (Var (xi, T)) =
(case lookup1 tenv (xi, T) of
SOME u => u
| NONE => Var (xi, substT T))
| subst (Bound _) = raise Same.SAME
| subst (Abs (a, T, t)) =
(Abs (a, substT T, Same.commit subst t)
handle Same.SAME => Abs (a, T, subst t))
| subst (t $ u) = (subst t $ Same.commit subst u handle Same.SAME => t $ subst u);
in subst end;
in
fun subst_type_same tyenv T =
if Vartab.is_empty tyenv then raise Same.SAME
else subst_type0 tyenv T;
fun subst_term_types_same tyenv t =
if Vartab.is_empty tyenv then raise Same.SAME
else Term_Subst.map_types_same (subst_type0 tyenv) t;
fun subst_term_same (tyenv, tenv) =
if Vartab.is_empty tenv then subst_term_types_same tyenv
else if Vartab.is_empty tyenv then subst_term1 tenv
else subst_term2 tenv tyenv;
fun subst_type tyenv T = subst_type_same tyenv T handle Same.SAME => T;
fun subst_term_types tyenv t = subst_term_types_same tyenv t handle Same.SAME => t;
fun subst_term envs t = subst_term_same envs t handle Same.SAME => t;
end;
(** expand defined atoms -- with local beta reduction **)
fun expand_atom T (U, u) =
subst_term_types (Type.raw_match (U, T) Vartab.empty) u
handle Type.TYPE_MATCH => raise TYPE ("expand_atom: ill-typed replacement", [T, U], [u]);
fun expand_term get =
let
fun expand tm =
let
val (head, args) = Term.strip_comb tm;
val args' = map expand args;
fun comb head' = Term.list_comb (head', args');
in
(case head of
Abs (x, T, t) => comb (Abs (x, T, expand t))
| _ =>
(case get head of
SOME def => Term.betapplys (expand_atom (Term.fastype_of head) def, args')
| NONE => comb head))
end;
in expand end;
fun expand_term_frees defs =
let
val eqs = map (fn ((x, U), u) => (x, (U, u))) defs;
val get = fn Free (x, _) => AList.lookup (op =) eqs x | _ => NONE;
in expand_term get end;
end;