X-Git-Url: http://matita.cs.unibo.it/gitweb/?p=helm.git;a=blobdiff_plain;f=components%2Fng_kernel%2FnCicReduction.ml;fp=components%2Fng_kernel%2FnCicReduction.ml;h=db19c9c9617dd13d237c99bb941daa19ed351d87;hp=0000000000000000000000000000000000000000;hb=f61af501fb4608cc4fb062a0864c774e677f0d76;hpb=58ae1809c352e71e7b5530dc41e2bfc834e1aef1 diff --git a/components/ng_kernel/nCicReduction.ml b/components/ng_kernel/nCicReduction.ml new file mode 100644 index 000000000..db19c9c96 --- /dev/null +++ b/components/ng_kernel/nCicReduction.ml @@ -0,0 +1,620 @@ +(* + ||M|| This file is part of HELM, an Hypertextual, Electronic + ||A|| Library of Mathematics, developed at the Computer Science + ||T|| Department, University of Bologna, Italy. + ||I|| + ||T|| HELM is free software; you can redistribute it and/or + ||A|| modify it under the terms of the GNU General Public License + \ / version 2 or (at your option) any later version. + \ / This software is distributed as is, NO WARRANTY. + V_______________________________________________________________ *) + +(* $Id$ *) + +(* TODO unify exceptions *) + +exception WrongUriToInductiveDefinition;; +exception Impossible of int;; +exception ReferenceToConstant;; +exception ReferenceToVariable;; +exception ReferenceToCurrentProof;; +exception ReferenceToInductiveDefinition;; + +let debug = false +let profile = false +let debug_print s = if debug then prerr_endline (Lazy.force s) + +let fdebug = ref 1;; +let debug t env s = + let rec debug_aux t i = + let module C = Cic in + let module U = UriManager in + CicPp.ppobj (C.Variable ("DEBUG", None, t, [], [])) ^ "\n" ^ i + in + if !fdebug = 0 then + debug_print (lazy (s ^ "\n" ^ List.fold_right debug_aux (t::env) "")) +;; + +module type Strategy = + sig + type stack_term + type env_term + type config = int * env_term list * NCic.term * stack_term list + val to_env : + reduce: (config -> config) -> + unwind: (config -> NCic.term) -> + config -> env_term + val from_stack : stack_term -> config + val from_stack_list_for_unwind : + unwind: (config -> NCic.term) -> + stack_term list -> NCic.term list + val from_env : env_term -> config + val from_env_for_unwind : + unwind: (config -> NCic.term) -> + env_term -> NCic.term + val stack_to_env : + reduce: (config -> config) -> + unwind: (config -> NCic.term) -> + stack_term -> env_term + val compute_to_env : + reduce: (config -> config) -> + unwind: (config -> NCic.term) -> + int -> env_term list -> + NCic.term -> env_term + val compute_to_stack : + reduce: (config -> config) -> + unwind: (config -> NCic.term) -> + config -> stack_term + end +;; + +module CallByValueByNameForUnwind' = + struct + type config = int * env_term list * NCic.term * stack_term list + and stack_term = config lazy_t * NCic.term lazy_t (* cbv, cbn *) + and env_term = config lazy_t * NCic.term lazy_t (* cbv, cbn *) + let to_env ~reduce ~unwind c = lazy (reduce c),lazy (unwind c) + let from_stack (c,_) = Lazy.force c + let from_stack_list_for_unwind ~unwind:_ l = + List.map (function (_,c) -> Lazy.force c) l + let from_env (c,_) = Lazy.force c + let from_env_for_unwind ~unwind:_ (_,c) = Lazy.force c + let stack_to_env ~reduce:_ ~unwind:_ config = config + let compute_to_env ~reduce ~unwind k e t = + lazy (reduce (k,e,t,[])), lazy (unwind (k,e,t,[])) + let compute_to_stack ~reduce ~unwind config = + lazy (reduce config), lazy (unwind config) + end +;; + + +(* {{{ module CallByValueByNameForUnwind = + struct + type config = int * env_term list * ens_term Cic.explicit_named_substitution * Cic.term * stack_term list + and stack_term = config + and env_term = config * config (* cbv, cbn *) + and ens_term = config * config (* cbv, cbn *) + + let to_env c = c,c + let to_ens c = c,c + let from_stack config = config + let from_stack_list_for_unwind ~unwind l = List.map unwind l + let from_env (c,_) = c + let from_ens (c,_) = c + let from_env_for_unwind ~unwind (_,c) = unwind c + let from_ens_for_unwind ~unwind (_,c) = unwind c + let stack_to_env ~reduce ~unwind config = reduce config, (0,[],[],unwind config,[]) + let compute_to_env ~reduce ~unwind k e ens t = (k,e,ens,t,[]), (k,e,ens,t,[]) + let compute_to_stack ~reduce ~unwind config = config + end +;; + + +(* Old Machine *) +module CallByNameStrategy = + struct + type stack_term = Cic.term + type env_term = Cic.term + type ens_term = Cic.term + type config = int * env_term list * ens_term Cic.explicit_named_substitution * Cic.term * stack_term list + let to_env v = v + let to_ens v = v + let from_stack ~unwind v = v + let from_stack_list ~unwind l = l + let from_env v = v + let from_ens v = v + let from_env_for_unwind ~unwind v = v + let from_ens_for_unwind ~unwind v = v + let stack_to_env ~reduce ~unwind v = v + let compute_to_stack ~reduce ~unwind k e ens t = unwind k e ens t + let compute_to_env ~reduce ~unwind k e ens t = unwind k e ens t + end +;; + +module CallByNameStrategy = + struct + type config = int * env_term list * ens_term Cic.explicit_named_substitution * Cic.term * stack_term list + and stack_term = config + and env_term = config + and ens_term = config + + let to_env c = c + let to_ens c = c + let from_stack config = config + let from_stack_list_for_unwind ~unwind l = List.map unwind l + let from_env c = c + let from_ens c = c + let from_env_for_unwind ~unwind c = unwind c + let from_ens_for_unwind ~unwind c = unwind c + let stack_to_env ~reduce ~unwind config = 0,[],[],unwind config,[] + let compute_to_env ~reduce ~unwind k e ens t = k,e,ens,t,[] + let compute_to_stack ~reduce ~unwind config = config + end +;; + +module CallByValueStrategy = + struct + type stack_term = Cic.term + type env_term = Cic.term + type ens_term = Cic.term + type config = int * env_term list * ens_term Cic.explicit_named_substitution * Cic.term * stack_term list + let to_env v = v + let to_ens v = v + let from_stack ~unwind v = v + let from_stack_list ~unwind l = l + let from_env v = v + let from_ens v = v + let from_env_for_unwind ~unwind v = v + let from_ens_for_unwind ~unwind v = v + let stack_to_env ~reduce ~unwind v = v + let compute_to_stack ~reduce ~unwind k e ens t = reduce (k,e,ens,t,[]) + let compute_to_env ~reduce ~unwind k e ens t = reduce (k,e,ens,t,[]) + end +;; + +module CallByValueStrategyByNameOnConstants = + struct + type stack_term = Cic.term + type env_term = Cic.term + type ens_term = Cic.term + type config = int * env_term list * ens_term Cic.explicit_named_substitution * Cic.term * stack_term list + let to_env v = v + let to_ens v = v + let from_stack ~unwind v = v + let from_stack_list ~unwind l = l + let from_env v = v + let from_ens v = v + let from_env_for_unwind ~unwind v = v + let from_ens_for_unwind ~unwind v = v + let stack_to_env ~reduce ~unwind v = v + let compute_to_stack ~reduce ~unwind k e ens = + function + Cic.Const _ as t -> unwind k e ens t + | t -> reduce (k,e,ens,t,[]) + let compute_to_env ~reduce ~unwind k e ens = + function + Cic.Const _ as t -> unwind k e ens t + | t -> reduce (k,e,ens,t,[]) + end +;; + +module LazyCallByValueStrategy = + struct + type stack_term = Cic.term lazy_t + type env_term = Cic.term lazy_t + type ens_term = Cic.term lazy_t + type config = int * env_term list * ens_term Cic.explicit_named_substitution * Cic.term * stack_term list + let to_env v = lazy v + let to_ens v = lazy v + let from_stack ~unwind v = Lazy.force v + let from_stack_list ~unwind l = List.map (from_stack ~unwind) l + let from_env v = Lazy.force v + let from_ens v = Lazy.force v + let from_env_for_unwind ~unwind v = Lazy.force v + let from_ens_for_unwind ~unwind v = Lazy.force v + let stack_to_env ~reduce ~unwind v = v + let compute_to_stack ~reduce ~unwind k e ens t = lazy (reduce (k,e,ens,t,[])) + let compute_to_env ~reduce ~unwind k e ens t = lazy (reduce (k,e,ens,t,[])) + end +;; + +module LazyCallByValueStrategyByNameOnConstants = + struct + type stack_term = Cic.term lazy_t + type env_term = Cic.term lazy_t + type ens_term = Cic.term lazy_t + type config = int * env_term list * ens_term Cic.explicit_named_substitution * Cic.term * stack_term list + let to_env v = lazy v + let to_ens v = lazy v + let from_stack ~unwind v = Lazy.force v + let from_stack_list ~unwind l = List.map (from_stack ~unwind) l + let from_env v = Lazy.force v + let from_ens v = Lazy.force v + let from_env_for_unwind ~unwind v = Lazy.force v + let from_ens_for_unwind ~unwind v = Lazy.force v + let stack_to_env ~reduce ~unwind v = v + let compute_to_stack ~reduce ~unwind k e ens t = + lazy ( + match t with + Cic.Const _ as t -> unwind k e ens t + | t -> reduce (k,e,ens,t,[])) + let compute_to_env ~reduce ~unwind k e ens t = + lazy ( + match t with + Cic.Const _ as t -> unwind k e ens t + | t -> reduce (k,e,ens,t,[])) + end +;; + +module LazyCallByNameStrategy = + struct + type stack_term = Cic.term lazy_t + type env_term = Cic.term lazy_t + type ens_term = Cic.term lazy_t + type config = int * env_term list * ens_term Cic.explicit_named_substitution * Cic.term * stack_term list + let to_env v = lazy v + let to_ens v = lazy v + let from_stack ~unwind v = Lazy.force v + let from_stack_list ~unwind l = List.map (from_stack ~unwind) l + let from_env v = Lazy.force v + let from_ens v = Lazy.force v + let from_env_for_unwind ~unwind v = Lazy.force v + let from_ens_for_unwind ~unwind v = Lazy.force v + let stack_to_env ~reduce ~unwind v = v + let compute_to_stack ~reduce ~unwind k e ens t = lazy (unwind k e ens t) + let compute_to_env ~reduce ~unwind k e ens t = lazy (unwind k e ens t) + end +;; + +module + LazyCallByValueByNameOnConstantsWhenFromStack_ByNameStrategyWhenFromEnvOrEns += + struct + type stack_term = reduce:bool -> Cic.term + type env_term = reduce:bool -> Cic.term + type ens_term = reduce:bool -> Cic.term + type config = int * env_term list * ens_term Cic.explicit_named_substitution * Cic.term * stack_term list + let to_env v = + let value = lazy v in + fun ~reduce -> Lazy.force value + let to_ens v = + let value = lazy v in + fun ~reduce -> Lazy.force value + let from_stack ~unwind v = (v ~reduce:false) + let from_stack_list ~unwind l = List.map (from_stack ~unwind) l + let from_env v = (v ~reduce:true) + let from_ens v = (v ~reduce:true) + let from_env_for_unwind ~unwind v = (v ~reduce:true) + let from_ens_for_unwind ~unwind v = (v ~reduce:true) + let stack_to_env ~reduce ~unwind v = v + let compute_to_stack ~reduce ~unwind k e ens t = + let svalue = + lazy ( + match t with + Cic.Const _ as t -> unwind k e ens t + | t -> reduce (k,e,ens,t,[]) + ) in + let lvalue = + lazy (unwind k e ens t) + in + fun ~reduce -> + if reduce then Lazy.force svalue else Lazy.force lvalue + let compute_to_env ~reduce ~unwind k e ens t = + let svalue = + lazy ( + match t with + Cic.Const _ as t -> unwind k e ens t + | t -> reduce (k,e,ens,t,[]) + ) in + let lvalue = + lazy (unwind k e ens t) + in + fun ~reduce -> + if reduce then Lazy.force svalue else Lazy.force lvalue + end +;; + +module ClosuresOnStackByValueFromEnvOrEnsStrategy = + struct + type config = int * env_term list * ens_term Cic.explicit_named_substitution * Cic.term * stack_term list + and stack_term = config + and env_term = config + and ens_term = config + + let to_env config = config + let to_ens config = config + let from_stack config = config + let from_stack_list_for_unwind ~unwind l = List.map unwind l + let from_env v = v + let from_ens v = v + let from_env_for_unwind ~unwind config = unwind config + let from_ens_for_unwind ~unwind config = unwind config + let stack_to_env ~reduce ~unwind config = reduce config + let compute_to_env ~reduce ~unwind k e ens t = (k,e,ens,t,[]) + let compute_to_stack ~reduce ~unwind config = config + end +;; + +module ClosuresOnStackByValueFromEnvOrEnsByNameOnConstantsStrategy = + struct + type stack_term = + int * Cic.term list * Cic.term Cic.explicit_named_substitution * Cic.term + type env_term = Cic.term + type ens_term = Cic.term + type config = int * env_term list * ens_term Cic.explicit_named_substitution * Cic.term * stack_term list + let to_env v = v + let to_ens v = v + let from_stack ~unwind (k,e,ens,t) = unwind k e ens t + let from_stack_list ~unwind l = List.map (from_stack ~unwind) l + let from_env v = v + let from_ens v = v + let from_env_for_unwind ~unwind v = v + let from_ens_for_unwind ~unwind v = v + let stack_to_env ~reduce ~unwind (k,e,ens,t) = + match t with + Cic.Const _ as t -> unwind k e ens t + | t -> reduce (k,e,ens,t,[]) + let compute_to_env ~reduce ~unwind k e ens t = + unwind k e ens t + let compute_to_stack ~reduce ~unwind k e ens t = (k,e,ens,t) + end +;; + +}}} *) + +module Reduction(RS : Strategy) = + struct + type env = RS.env_term list + type stack = RS.stack_term list + type config = int * env * NCic.term * stack + + let rec unwind (k,e,t,s) = + let t = + if k = 0 then t + else + NCicSubstitution.psubst ~avoid_beta_redexes:true + true 0 (RS.from_env_for_unwind ~unwind) e t + in + if s = [] then t + else NCic.Appl(t::(RS.from_stack_list_for_unwind ~unwind s)) + ;; + + let list_nth l n = try List.nth l n with Failure _ -> assert false;; + let rec replace i s t = + match i,s with + | 0,_::tl -> t::tl + | n,he::tl -> he::(replace (n - 1) tl t) + | _,_ -> assert false + ;; + + let rec reduce ~delta ?(subst = []) context : config -> config = + let rec aux = function + | k, e, NCic.Rel n, s when n <= k -> + let k',e',t',s' = RS.from_env (list_nth e (n-1)) in + aux (k',e',t',s'@s) + | k, _, NCic.Rel n, s as config (* when n > k *) -> + (match List.nth context (n - 1 - k) with + | (_,NCic.Decl _) -> config + | (_,NCic.Def (x,_)) -> aux (0,[],NCicSubstitution.lift (n - k) x,s)) + | (k, e, NCic.Meta (n,l), s) as config -> + (try + let _,_, term,_ = NCicUtils.lookup_subst n subst in + aux (k, e, NCicSubstitution.subst_meta l term,s) + with NCicUtils.Subst_not_found _ -> config) + | (_, _, NCic.Sort _, _) as config -> config + | (_, _, NCic.Implicit _, _) -> assert false + | (_, _, NCic.Prod _, _) as config -> config + | (_, _, NCic.Lambda _, []) as config -> config + | (k, e, NCic.Lambda (_,_,t), p::s) -> + aux (k+1, (RS.stack_to_env ~reduce:aux ~unwind p)::e, t,s) + | (k, e, NCic.LetIn (_,_,m,t), s) -> + let m' = RS.compute_to_env ~reduce:aux ~unwind k e m in + aux (k+1, m'::e, t, s) + | (_, _, NCic.Appl [], _) -> assert false + | (k, e, NCic.Appl (he::tl), s) -> + let tl' = + List.map (fun t->RS.compute_to_stack ~reduce:aux ~unwind (k,e,t,[])) tl + in + aux (k, e, he, tl' @ s) + | (_, _, NCic.Const + (NReference.Ref (_,_,NReference.Def) as refer), s) as config -> + let _,_,body,_,_,height = NCicEnvironment.get_checked_def refer in + if delta > height then config else aux (0, [], body, s) + | (_, _, NCic.Const (NReference.Ref + (_,_,NReference.Fix (fixno,recindex)) as refer),s) as config -> + let fixes,_, height = NCicEnvironment.get_checked_fixes refer in + if delta > height then config else + (match + try Some (RS.from_stack (List.nth s recindex)) + with Failure _ -> None + with + | None -> config + | Some recparam -> + match reduce ~delta:0 ~subst context recparam with + | (_,_,NCic.Const (NReference.Ref (_,_,NReference.Con _)), _) as c -> + let new_s = + replace recindex s (RS.compute_to_stack ~reduce:aux ~unwind c) + in + let _,_,_,_,body = List.nth fixes fixno in + aux (0, [], body, new_s) + | _ -> config) + | (_, _, NCic.Const _, _) as config -> config + | (k, e, NCic.Match (_,_,term,pl),s) as config -> + let decofix = function + | (_,_,NCic.Const(NReference.Ref(_,_,NReference.CoFix c)as refer),s)-> + let cofixes,_,_ = NCicEnvironment.get_checked_cofixes refer in + let _,_,_,_,body = List.nth cofixes c in + reduce ~delta:0 ~subst context (0,[],body,s) + | config -> config + in + (match decofix (reduce ~delta:0 ~subst context (k,e,term,[])) with + | (_, _, NCic.Const (NReference.Ref (_,_,NReference.Con (_,j))),[]) -> + aux (k, e, List.nth pl (j-1), s) + | (_, _, NCic.Const + (NReference.Ref (_,_,NReference.Con (_,j)) as refer), s') -> + let leftno = NCicEnvironment.get_indty_leftno refer in + let _,params = HExtlib.split_nth leftno s' in + aux (k, e, List.nth pl (j-1), params@s) + | _ -> config) + in + aux + ;; + + let whd ?(delta=0) ?(subst=[]) context t = + unwind (reduce ~delta ~subst context (0, [], t, [])) + ;; + + end +;; + + +(* {{{ ROTTO = rompe l'unificazione poiche' riduce gli argomenti di un'applicazione + senza ridurre la testa +module R = Reduction CallByNameStrategy;; OK 56.368s +module R = Reduction CallByValueStrategy;; ROTTO +module R = Reduction CallByValueStrategyByNameOnConstants;; ROTTO +module R = Reduction LazyCallByValueStrategy;; ROTTO +module R = Reduction LazyCallByValueStrategyByNameOnConstants;; ROTTO +module R = Reduction LazyCallByNameStrategy;; OK 0m56.398s +module R = Reduction + LazyCallByValueByNameOnConstantsWhenFromStack_ByNameStrategyWhenFromEnvOrEns;; + OK 59.058s +module R = Reduction ClosuresOnStackByValueFromEnvOrEnsStrategy;; OK 58.583s +module R = Reduction + ClosuresOnStackByValueFromEnvOrEnsByNameOnConstantsStrategy;; OK 58.094s +module R = Reduction(ClosuresOnStackByValueFromEnvOrEnsStrategy);; OK 58.127s +module R = Reduction(CallByValueByNameForUnwind);; +module R = Reduction(CallByNameStrategy);; +module R = Reduction(ClosuresOnStackByValueFromEnvOrEnsStrategy);;}}} *) +module RS = CallByValueByNameForUnwind';; + +module R = Reduction(RS);; +module U = UriManager;; + +let whd = R.whd + +(* +let whd = + let profiler_whd = HExtlib.profile ~enable:profile "are_convertible.whd" in + fun ?(delta=true) ?(subst=[]) context t -> + profiler_whd.HExtlib.profile (whd ~delta ~subst context) t +*) + +(* mimic ocaml (<< 3.08) "=" behaviour. Tests physical equality first then + * fallbacks to structural equality *) +let (===) x y = Pervasives.compare x y = 0 ;; + +module C = NCic + +(* t1, t2 must be well-typed *) +let are_convertible whd ?(subst=[]) ?(metasenv=[]) = + let rec aux test_equality_only context t1 t2 = + let rec aux2 test_equality_only t1 t2 = + if t1 === t2 then + true + else + match (t1,t2) with + | (C.Sort (C.Type a), C.Sort (C.Type b)) -> a <= b + | (C.Sort s1,C.Sort (C.Type _)) -> (not test_equality_only) + | (C.Sort s1, C.Sort s2) -> s1 = s2 + + | (C.Prod (name1,s1,t1), C.Prod(_,s2,t2)) -> + aux true context s1 s2 && + aux test_equality_only ((name1, C.Decl s1)::context) t1 t2 + | (C.Lambda (name1,s1,t1), C.Lambda(_,s2,t2)) -> + aux true context s1 s2 && + aux true ((name1, C.Decl s1)::context) t1 t2 + | (C.LetIn (name1,ty1,s1,t1), C.LetIn(_,ty2,s2,t2)) -> + aux test_equality_only context ty1 ty2 && + aux test_equality_only context s1 s2 && + aux test_equality_only ((name1, C.Def (s1,ty1))::context) t1 t2 + + | (C.Meta (n1,(s1, C.Irl i1)), C.Meta (n2,(s2, C.Irl i2))) + when n1 = n2 && s1 = s2 -> true + | (C.Meta (n1,(s1, l1)), C.Meta (n2,(s2, l2))) when n1 = n2 -> + let l1 = NCicUtils.expand_local_context l1 in + let l2 = NCicUtils.expand_local_context l2 in + (try List.for_all2 + (fun t1 t2 -> aux test_equality_only context + (NCicSubstitution.lift s1 t1) + (NCicSubstitution.lift s2 t2)) + l1 l2 + with Invalid_argument _ -> false) + + | C.Meta (n1,l1), _ -> + (try + let _,_,term,_ = NCicUtils.lookup_subst n1 subst in + let term = NCicSubstitution.subst_meta l1 term in + aux test_equality_only context term t2 + with NCicUtils.Subst_not_found _ -> false) + | _, C.Meta (n2,l2) -> + (try + let _,_,term,_ = NCicUtils.lookup_subst n2 subst in + let term = NCicSubstitution.subst_meta l2 term in + aux test_equality_only context t1 term + with NCicUtils.Subst_not_found _ -> false) + + | (C.Appl l1, C.Appl l2) -> + (try List.for_all2 (aux test_equality_only context) l1 l2 + with Invalid_argument _ -> false) + + | (C.Match (ref1,outtype1,term1,pl1), + C.Match (ref2,outtype2,term2,pl2)) -> + NReference.eq ref1 ref2 && + aux test_equality_only context outtype1 outtype2 && + aux test_equality_only context term1 term2 && + (try List.for_all2 (aux test_equality_only context) pl1 pl2 + with Invalid_argument _ -> false) + + | (C.Implicit _, _) | (_, C.Implicit _) -> assert false + | (_,_) -> false + in + if aux2 test_equality_only t1 t2 then + true + else + let rec convert_machines = function + | [] -> true + | ((k1,env1,h1,s1),(k2,env2,h2,s2))::tl -> + aux2 test_equality_only + (R.unwind (k1,env1,h1,[])) (R.unwind (k2,env2,h2,[])) && + let problems = + let red_stack = + List.map + (fun si-> R.reduce ~delta:0 ~subst context(RS.from_stack si)) + in + try Some (List.combine (red_stack s1) (red_stack s2) @ tl) + with Invalid_argument _ -> None + in + match problems with + | None -> false + | Some problems -> convert_machines problems + in + convert_machines + [R.reduce ~delta:0 ~subst context (0,[],t1,[]), + R.reduce ~delta:0 ~subst context (0,[],t2,[])] + in + aux false +;; + +let are_convertible = are_convertible whd + +let rec head_beta_reduce ?(delta=max_int) ?(upto=(-1)) t l = + match upto, t, l with + | 0, C.Appl l1, _ -> C.Appl (l1 @ l) + | 0, t, [] -> t + | 0, t, _ -> C.Appl (t::l) + | _, C.Appl (hd::tl), _ -> head_beta_reduce ~delta ~upto hd (tl @ l) + | _, C.Lambda(_,_,bo), arg::tl -> + let bo = NCicSubstitution.subst arg bo in + head_beta_reduce ~delta ~upto:(upto - 1) bo tl + | _, C.Const (NReference.Ref (height, _, NReference.Def) as re), _ + when delta <= height -> + let _, _, bo, _, _, _ = NCicEnvironment.get_checked_def re in + head_beta_reduce ~upto ~delta bo l + | _, t, [] -> t + | _, t, _ -> C.Appl (t::l) +;; + +let head_beta_reduce ?delta ?upto t = head_beta_reduce ?delta ?upto t [];; + +(* vim:set foldmethod=marker: *)