(* ========================================================================= *)
(* Complete HOL kernel of types, terms and theorems. *)
(* *)
(* John Harrison, University of Cambridge Computer Laboratory *)
(* *)
(* (c) Copyright, University of Cambridge 1998 *)
(* (c) Copyright, John Harrison 1998-2007 *)
(* ========================================================================= *)
needs "lib.ml";;
module type Hol_kernel =
sig
type hol_type = private
Tyvar of string
| Tyapp of string * hol_type list
type term = private
Var of string * hol_type
| Const of string * hol_type
| Comb of term * term
| Abs of term * term
type thm
val types: unit -> (string * int)list
val get_type_arity : string -> int
val new_type : (string * int) -> unit
val mk_type: (string * hol_type list) -> hol_type
val mk_vartype : string -> hol_type
val dest_type : hol_type -> (string * hol_type list)
val dest_vartype : hol_type -> string
val is_type : hol_type -> bool
val is_vartype : hol_type -> bool
val tyvars : hol_type -> hol_type list
val type_subst : (hol_type * hol_type)list -> hol_type -> hol_type
val bool_ty : hol_type
val aty : hol_type
val constants : unit -> (string * hol_type) list
val get_const_type : string -> hol_type
val new_constant : string * hol_type -> unit
val type_of : term -> hol_type
val alphaorder : term -> term -> int
val is_var : term -> bool
val is_const : term -> bool
val is_abs : term -> bool
val is_comb : term -> bool
val mk_var : string * hol_type -> term
val mk_const : string * (hol_type * hol_type) list -> term
val mk_abs : term * term -> term
val mk_comb : term * term -> term
val dest_var : term -> string * hol_type
val dest_const : term -> string * hol_type
val dest_comb : term -> term * term
val dest_abs : term -> term * term
val frees : term -> term list
val freesl : term list -> term list
val freesin : term list -> term -> bool
val vfree_in : term -> term -> bool
val type_vars_in_term : term -> hol_type list
val variant : term list -> term -> term
val vsubst : (term * term) list -> term -> term
val inst : (hol_type * hol_type) list -> term -> term
val rand: term -> term
val rator: term -> term
val dest_eq: term -> term * term
val dest_thm : thm -> term list * term
val hyp : thm -> term list
val concl : thm -> term
val REFL : term -> thm
val TRANS : thm -> thm -> thm
val MK_COMB : thm * thm -> thm
val ABS : term -> thm -> thm
val BETA : term -> thm
val ASSUME : term -> thm
val EQ_MP : thm -> thm -> thm
val DEDUCT_ANTISYM_RULE : thm -> thm -> thm
val INST_TYPE : (hol_type * hol_type) list -> thm -> thm
val INST : (term * term) list -> thm -> thm
val axioms : unit -> thm list
val new_axiom : term -> thm
val definitions : unit -> thm list
val new_basic_definition : term -> thm
val new_basic_type_definition :
string -> string * string -> thm -> thm * thm
end;;
(* ------------------------------------------------------------------------- *)
(* This is the implementation of those primitives. *)
(* ------------------------------------------------------------------------- *)
module Hol : Hol_kernel = struct
type hol_type = Tyvar of string
| Tyapp of string * hol_type list
type term = Var of string * hol_type
| Const of string * hol_type
| Comb of term * term
| Abs of term * term
type thm = Sequent of (term list * term)
(* ------------------------------------------------------------------------- *)
(* List of current type constants with their arities. *)
(* *)
(* Initially we just have the boolean type and the function space *)
(* constructor. Later on we add as primitive the type of individuals. *)
(* All other new types result from definitional extension. *)
(* ------------------------------------------------------------------------- *)
let the_type_constants = ref ["bool",0; "fun",2]
(* ------------------------------------------------------------------------- *)
(* Return all the defined types. *)
(* ------------------------------------------------------------------------- *)
let types() = !the_type_constants
(* ------------------------------------------------------------------------- *)
(* Lookup function for type constants. Returns arity if it succeeds. *)
(* ------------------------------------------------------------------------- *)
let get_type_arity s = assoc s (!the_type_constants)
(* ------------------------------------------------------------------------- *)
(* Declare a new type. *)
(* ------------------------------------------------------------------------- *)
let new_type(name,arity) =
if can get_type_arity name then
failwith ("new_type: type "^name^" has already been declared")
else the_type_constants := (name,arity)::(!the_type_constants)
(* ------------------------------------------------------------------------- *)
(* Basic type constructors. *)
(* ------------------------------------------------------------------------- *)
let mk_type(tyop,args) =
let arity = try get_type_arity tyop with Failure _ ->
failwith ("mk_type: type "^tyop^" has not been defined") in
if arity = length args then
Tyapp(tyop,args)
else failwith ("mk_type: wrong number of arguments to "^tyop)
let mk_vartype v = Tyvar(v)
(* ------------------------------------------------------------------------- *)
(* Basic type destructors. *)
(* ------------------------------------------------------------------------- *)
let dest_type =
function
(Tyapp (s,ty)) -> s,ty
| (Tyvar _) -> failwith "dest_type: type variable not a constructor"
let dest_vartype =
function
(Tyapp(_,_)) -> failwith "dest_vartype: type constructor not a variable"
| (Tyvar s) -> s
(* ------------------------------------------------------------------------- *)
(* Basic type discriminators. *)
(* ------------------------------------------------------------------------- *)
let is_type = can dest_type
let is_vartype = can dest_vartype
(* ------------------------------------------------------------------------- *)
(* Return the type variables in a type and in a list of types. *)
(* ------------------------------------------------------------------------- *)
let rec tyvars =
function
(Tyapp(_,args)) -> itlist (union o tyvars) args []
| (Tyvar v as tv) -> [tv]
(* ------------------------------------------------------------------------- *)
(* Substitute types for type variables. *)
(* *)
(* NB: non-variables in subst list are just ignored (a check would be *)
(* repeated many times), as are repetitions (first possibility is taken). *)
(* ------------------------------------------------------------------------- *)
let rec type_subst i ty =
match ty with
Tyapp(tycon,args) ->
let args' = qmap (type_subst i) args in
if args' == args then ty else Tyapp(tycon,args')
| _ -> rev_assocd ty i ty
let bool_ty = Tyapp("bool",[])
let aty = Tyvar "A"
(* ------------------------------------------------------------------------- *)
(* List of term constants and their types. *)
(* *)
(* We begin with just equality (over all types). Later, the Hilbert choice *)
(* operator is added. All other new constants are defined. *)
(* ------------------------------------------------------------------------- *)
let the_term_constants =
ref ["=",Tyapp("fun",[aty;Tyapp("fun",[aty;bool_ty])])]
(* ------------------------------------------------------------------------- *)
(* Return all the defined constants with generic types. *)
(* ------------------------------------------------------------------------- *)
let constants() = !the_term_constants
(* ------------------------------------------------------------------------- *)
(* Gets type of constant if it succeeds. *)
(* ------------------------------------------------------------------------- *)
let get_const_type s = assoc s (!the_term_constants)
(* ------------------------------------------------------------------------- *)
(* Declare a new constant. *)
(* ------------------------------------------------------------------------- *)
let new_constant(name,ty) =
if can get_const_type name then
failwith ("new_constant: constant "^name^" has already been declared")
else the_term_constants := (name,ty)::(!the_term_constants)
(* ------------------------------------------------------------------------- *)
(* Finds the type of a term (assumes it is well-typed). *)
(* ------------------------------------------------------------------------- *)
let rec type_of tm =
match tm with
Var(_,ty) -> ty
| Const(_,ty) -> ty
| Comb(s,_) -> hd(tl(snd(dest_type(type_of s))))
| Abs(Var(_,ty),t) -> Tyapp("fun",[ty;type_of t])
(* ------------------------------------------------------------------------- *)
(* Primitive discriminators. *)
(* ------------------------------------------------------------------------- *)
let is_var = function (Var(_,_)) -> true | _ -> false
let is_const = function (Const(_,_)) -> true | _ -> false
let is_abs = function (Abs(_,_)) -> true | _ -> false
let is_comb = function (Comb(_,_)) -> true | _ -> false
(* ------------------------------------------------------------------------- *)
(* Primitive constructors. *)
(* ------------------------------------------------------------------------- *)
let mk_var(v,ty) = Var(v,ty)
let mk_const(name,theta) =
let uty = try get_const_type name with Failure _ ->
failwith "mk_const: not a constant name" in
Const(name,type_subst theta uty)
let mk_abs(bvar,bod) =
match bvar with
Var(_,_) -> Abs(bvar,bod)
| _ -> failwith "mk_abs: not a variable"
let mk_comb(f,a) =
match type_of f with
Tyapp("fun",[ty;_]) when Pervasives.compare ty (type_of a) = 0
-> Comb(f,a)
| _ -> failwith "mk_comb: types do not agree"
(* ------------------------------------------------------------------------- *)
(* Primitive destructors. *)
(* ------------------------------------------------------------------------- *)
let dest_var =
function (Var(s,ty)) -> s,ty | _ -> failwith "dest_var: not a variable"
let dest_const =
function (Const(s,ty)) -> s,ty | _ -> failwith "dest_const: not a constant"
let dest_comb =
function (Comb(f,x)) -> f,x | _ -> failwith "dest_comb: not a combination"
let dest_abs =
function (Abs(v,b)) -> v,b | _ -> failwith "dest_abs: not an abstraction"
(* ------------------------------------------------------------------------- *)
(* Finds the variables free in a term (list of terms). *)
(* ------------------------------------------------------------------------- *)
let rec frees tm =
match tm with
Var(_,_) -> [tm]
| Const(_,_) -> []
| Abs(bv,bod) -> subtract (frees bod) [bv]
| Comb(s,t) -> union (frees s) (frees t)
let freesl tml = itlist (union o frees) tml []
(* ------------------------------------------------------------------------- *)
(* Whether all free variables in a term appear in a list. *)
(* ------------------------------------------------------------------------- *)
let rec freesin acc tm =
match tm with
Var(_,_) -> mem tm acc
| Const(_,_) -> true
| Abs(bv,bod) -> freesin (bv::acc) bod
| Comb(s,t) -> freesin acc s & freesin acc t
(* ------------------------------------------------------------------------- *)
(* Whether a variable (or constant in fact) is free in a term. *)
(* ------------------------------------------------------------------------- *)
let rec vfree_in v tm =
match tm with
Abs(bv,bod) -> v <> bv & vfree_in v bod
| Comb(s,t) -> vfree_in v s or vfree_in v t
| _ -> Pervasives.compare tm v = 0
(* ------------------------------------------------------------------------- *)
(* Finds the type variables (free) in a term. *)
(* ------------------------------------------------------------------------- *)
let rec type_vars_in_term tm =
match tm with
Var(_,ty) -> tyvars ty
| Const(_,ty) -> tyvars ty
| Comb(s,t) -> union (type_vars_in_term s) (type_vars_in_term t)
| Abs(Var(_,ty),t) -> union (tyvars ty) (type_vars_in_term t)
(* ------------------------------------------------------------------------- *)
(* For name-carrying syntax, we need this early. *)
(* ------------------------------------------------------------------------- *)
let rec variant avoid v =
if not(exists (vfree_in v) avoid) then v else
match v with
Var(s,ty) -> variant avoid (Var(s^"'",ty))
| _ -> failwith "variant: not a variable"
(* ------------------------------------------------------------------------- *)
(* Substitution primitive (substitution for variables only!) *)
(* ------------------------------------------------------------------------- *)
let vsubst =
let rec vsubst ilist tm =
match tm with
Var(_,_) -> rev_assocd tm ilist tm
| Const(_,_) -> tm
| Comb(s,t) -> let s' = vsubst ilist s and t' = vsubst ilist t in
if s' == s & t' == t then tm else Comb(s',t')
| Abs(v,s) -> let ilist' = filter (fun (t,x) -> x <> v) ilist in
if ilist' = [] then tm else
let s' = vsubst ilist' s in
if s' == s then tm else
if exists (fun (t,x) -> vfree_in v t & vfree_in x s) ilist'
then let v' = variant [s'] v in
Abs(v',vsubst ((v',v)::ilist') s)
else Abs(v,s') in
fun theta ->
if theta = [] then (fun tm -> tm) else
if forall (fun (t,x) -> type_of t = snd(dest_var x)) theta
then vsubst theta else failwith "vsubst: Bad substitution list"
(* ------------------------------------------------------------------------- *)
(* Type instantiation primitive. *)
(* ------------------------------------------------------------------------- *)
exception Clash of term
let inst =
let rec inst env tyin tm =
match tm with
Var(n,ty) -> let ty' = type_subst tyin ty in
let tm' = if ty' == ty then tm else Var(n,ty') in
if Pervasives.compare (rev_assocd tm' env tm) tm = 0
then tm'
else raise (Clash tm')
| Const(c,ty) -> let ty' = type_subst tyin ty in
if ty' == ty then tm else Const(c,ty')
| Comb(f,x) -> let f' = inst env tyin f and x' = inst env tyin x in
if f' == f & x' == x then tm else Comb(f',x')
| Abs(y,t) -> let y' = inst [] tyin y in
let env' = (y,y')::env in
try let t' = inst env' tyin t in
if y' == y & t' == t then tm else Abs(y',t')
with (Clash(w') as ex) ->
if w' <> y' then raise ex else
let ifrees = map (inst [] tyin) (frees t) in
let y'' = variant ifrees y' in
let z = Var(fst(dest_var y''),snd(dest_var y)) in
inst env tyin (Abs(z,vsubst[z,y] t)) in
fun tyin -> if tyin = [] then fun tm -> tm else inst [] tyin
(* ------------------------------------------------------------------------- *)
(* A few bits of general derived syntax. *)
(* ------------------------------------------------------------------------- *)
let rator tm =
match tm with
Comb(l,r) -> l
| _ -> failwith "rator: Not a combination"
let rand tm =
match tm with
Comb(l,r) -> r
| _ -> failwith "rand: Not a combination"
(* ------------------------------------------------------------------------- *)
(* Syntax operations for equations. *)
(* ------------------------------------------------------------------------- *)
let safe_mk_eq l r =
let ty = type_of l in
Comb(Comb(Const("=",Tyapp("fun",[ty;Tyapp("fun",[ty;bool_ty])])),l),r)
let dest_eq tm =
match tm with
Comb(Comb(Const("=",_),l),r) -> l,r
| _ -> failwith "dest_eq"
(* ------------------------------------------------------------------------- *)
(* Useful to have term union modulo alpha-conversion for assumption lists. *)
(* ------------------------------------------------------------------------- *)
let rec ordav env x1 x2 =
match env with
[] -> Pervasives.compare x1 x2
| (t1,t2)::oenv -> if Pervasives.compare x1 t1 = 0
then if Pervasives.compare x2 t2 = 0
then 0 else -1
else if Pervasives.compare x2 t2 = 0 then 1
else ordav oenv x1 x2
let rec orda env tm1 tm2 =
if tm1 == tm2 & forall (fun (x,y) -> x = y) env then 0 else
match (tm1,tm2) with
Var(x1,ty1),Var(x2,ty2) -> ordav env tm1 tm2
| Const(x1,ty1),Const(x2,ty2) -> Pervasives.compare tm1 tm2
| Comb(s1,t1),Comb(s2,t2) ->
let c = orda env s1 s2 in if c <> 0 then c else orda env t1 t2
| Abs(Var(_,ty1) as x1,t1),Abs(Var(_,ty2) as x2,t2) ->
let c = Pervasives.compare ty1 ty2 in
if c <> 0 then c else orda ((x1,x2)::env) t1 t2
| Const(_,_),_ -> -1
| _,Const(_,_) -> 1
| Var(_,_),_ -> -1
| _,Var(_,_) -> 1
| Comb(_,_),_ -> -1
| _,Comb(_,_) -> 1
let alphaorder = orda []
let rec term_union l1 l2 =
match (l1,l2) with
([],l2) -> l2
| (l1,[]) -> l1
| (h1::t1,h2::t2) -> let c = alphaorder h1 h2 in
if c = 0 then h1::(term_union t1 t2)
else if c < 0 then h1::(term_union t1 l2)
else h2::(term_union l1 t2)
let rec term_remove t l =
match l with
s::ss -> let c = alphaorder t s in
if c > 0 then
let ss' = term_remove t ss in
if ss' == ss then l else s::ss'
else if c = 0 then ss else l
| [] -> l
let rec term_image f l =
match l with
h::t -> let h' = f h and t' = term_image f t in
if h' == h & t' == t then l else term_union [h'] t'
| [] -> l
(* ------------------------------------------------------------------------- *)
(* Basic theorem destructors. *)
(* ------------------------------------------------------------------------- *)
let dest_thm (Sequent(asl,c)) = (asl,c)
let hyp (Sequent(asl,c)) = asl
let concl (Sequent(asl,c)) = c
(* ------------------------------------------------------------------------- *)
(* Basic equality properties; TRANS is derivable but included for efficiency *)
(* ------------------------------------------------------------------------- *)
let REFL tm =
Sequent([],safe_mk_eq tm tm)
let TRANS (Sequent(asl1,c1)) (Sequent(asl2,c2)) =
match (c1,c2) with
Comb((Comb(Const("=",_),_) as eql),m1),Comb(Comb(Const("=",_),m2),r)
when alphaorder m1 m2 = 0 -> Sequent(term_union asl1 asl2,Comb(eql,r))
| _ -> failwith "TRANS"
(* ------------------------------------------------------------------------- *)
(* Congruence properties of equality. *)
(* ------------------------------------------------------------------------- *)
let MK_COMB(Sequent(asl1,c1),Sequent(asl2,c2)) =
match (c1,c2) with
Comb(Comb(Const("=",_),l1),r1),Comb(Comb(Const("=",_),l2),r2) ->
(match type_of l1 with
Tyapp("fun",[ty;_]) when Pervasives.compare ty (type_of l2) = 0
-> Sequent(term_union asl1 asl2,
safe_mk_eq (Comb(l1,l2)) (Comb(r1,r2)))
| _ -> failwith "MK_COMB: types do not agree")
| _ -> failwith "MK_COMB: not both equations"
let ABS v (Sequent(asl,c)) =
match (v,c) with
Var(_,_),Comb(Comb(Const("=",_),l),r) when not(exists (vfree_in v) asl)
-> Sequent(asl,safe_mk_eq (Abs(v,l)) (Abs(v,r)))
| _ -> failwith "ABS";;
(* ------------------------------------------------------------------------- *)
(* Trivial case of lambda calculus beta-conversion. *)
(* ------------------------------------------------------------------------- *)
let BETA tm =
match tm with
Comb(Abs(v,bod),arg) when Pervasives.compare arg v = 0
-> Sequent([],safe_mk_eq tm bod)
| _ -> failwith "BETA: not a trivial beta-redex"
(* ------------------------------------------------------------------------- *)
(* Rules connected with deduction. *)
(* ------------------------------------------------------------------------- *)
let ASSUME tm =
if Pervasives.compare (type_of tm) bool_ty = 0 then Sequent([tm],tm)
else failwith "ASSUME: not a proposition"
let EQ_MP (Sequent(asl1,eq)) (Sequent(asl2,c)) =
match eq with
Comb(Comb(Const("=",_),l),r) when alphaorder l c = 0
-> Sequent(term_union asl1 asl2,r)
| _ -> failwith "EQ_MP"
let DEDUCT_ANTISYM_RULE (Sequent(asl1,c1)) (Sequent(asl2,c2)) =
let asl1' = term_remove c2 asl1 and asl2' = term_remove c1 asl2 in
Sequent(term_union asl1' asl2',safe_mk_eq c1 c2)
(* ------------------------------------------------------------------------- *)
(* Type and term instantiation. *)
(* ------------------------------------------------------------------------- *)
let INST_TYPE theta (Sequent(asl,c)) =
let inst_fn = inst theta in
Sequent(term_image inst_fn asl,inst_fn c)
let INST theta (Sequent(asl,c)) =
let inst_fun = vsubst theta in
Sequent(term_image inst_fun asl,inst_fun c)
(* ------------------------------------------------------------------------- *)
(* Handling of axioms. *)
(* ------------------------------------------------------------------------- *)
let the_axioms = ref ([]:thm list)
let axioms() = !the_axioms
let new_axiom tm =
if Pervasives.compare (type_of tm) bool_ty = 0 then
let th = Sequent([],tm) in
(the_axioms := th::(!the_axioms); th)
else failwith "new_axiom: Not a proposition"
(* ------------------------------------------------------------------------- *)
(* Handling of (term) definitions. *)
(* ------------------------------------------------------------------------- *)
let the_definitions = ref ([]:thm list)
let definitions() = !the_definitions
let new_basic_definition tm =
match tm with
Comb(Comb(Const("=",_),Var(cname,ty)),r) ->
if not(freesin [] r) then failwith "new_definition: term not closed"
else if not (subset (type_vars_in_term r) (tyvars ty))
then failwith "new_definition: Type variables not reflected in constant"
else let c = new_constant(cname,ty); Const(cname,ty) in
let dth = Sequent([],safe_mk_eq c r) in
the_definitions := dth::(!the_definitions); dth
| _ -> failwith "new_basic_definition"
(* ------------------------------------------------------------------------- *)
(* Handling of type definitions. *)
(* *)
(* This function now involves no logical constants beyond equality. *)
(* *)
(* |- P t *)
(* --------------------------- *)
(* |- abs(rep a) = a *)
(* |- P r = (rep(abs r) = r) *)
(* *)
(* Where "abs" and "rep" are new constants with the nominated names. *)
(* ------------------------------------------------------------------------- *)
let new_basic_type_definition tyname (absname,repname) (Sequent(asl,c)) =
if exists (can get_const_type) [absname; repname] then
failwith "new_basic_type_definition: Constant(s) already in use" else
if not (asl = []) then
failwith "new_basic_type_definition: Assumptions in theorem" else
let P,x = try dest_comb c
with Failure _ ->
failwith "new_basic_type_definition: Not a combination" in
if not(freesin [] P) then
failwith "new_basic_type_definition: Predicate is not closed" else
let tyvars = sort (<=) (type_vars_in_term P) in
let _ = try new_type(tyname,length tyvars)
with Failure _ ->
failwith "new_basic_type_definition: Type already defined" in
let aty = Tyapp(tyname,tyvars)
and rty = type_of x in
let absty = Tyapp("fun",[rty;aty]) and repty = Tyapp("fun",[aty;rty]) in
let abs = (new_constant(absname,absty); Const(absname,absty))
and rep = (new_constant(repname,repty); Const(repname,repty)) in
let a = Var("a",aty) and r = Var("r",rty) in
Sequent([],safe_mk_eq (Comb(abs,mk_comb(rep,a))) a),
Sequent([],safe_mk_eq (Comb(P,r))
(safe_mk_eq (mk_comb(rep,mk_comb(abs,r))) r))
end;;
include Hol;;
(* ------------------------------------------------------------------------- *)
(* Stuff that didn't seem worth putting in. *)
(* ------------------------------------------------------------------------- *)
let mk_fun_ty ty1 ty2 = mk_type("fun",[ty1; ty2]);;
let bty = mk_vartype "B";;
let is_eq tm =
match tm with
Comb(Comb(Const("=",_),_),_) -> true
| _ -> false;;
let mk_eq =
let eq = mk_const("=",[]) in
fun (l,r) ->
try let ty = type_of l in
let eq_tm = inst [ty,aty] eq in
mk_comb(mk_comb(eq_tm,l),r)
with Failure _ -> failwith "mk_eq";;
(* ------------------------------------------------------------------------- *)
(* Tests for alpha-convertibility (equality ignoring names in abstractions). *)
(* ------------------------------------------------------------------------- *)
let aconv s t = alphaorder s t = 0;;
(* ------------------------------------------------------------------------- *)
(* Comparison function on theorems. Currently the same as equality, but *)
(* it's useful to separate because in the proof-recording version it isn't. *)
(* ------------------------------------------------------------------------- *)
let equals_thm th th' = dest_thm th = dest_thm th';;