123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324(* This file is free software. See file "license" for more details. *)(** {1 Lambda-Calculus} *)letprof_whnf=ZProf.make"term.whnf"letprof_snf=ZProf.make"term.snf"letprof_eta_expand=ZProf.make"term.eta_expand"letprof_eta_reduce=ZProf.make"term.eta_reduce"letsection=Util.Section.make"lambdas"moduleInner=structmoduleT=InnerTermtypeterm=T.ttypestate={head:T.t;(* not an app *)env:T.tDBEnv.t;(* env for the head *)args:T.tlist;(* arguments, with their own env *)ty:T.t;(* type *)}(* evaluate term in environment *)leteval_in_env_envt:T.t=T.DB.evalenvtletnormalizest=matchT.viewst.headwith|T.App(f,l)->(* the arguments in [l] might contain variables *)letl=List.rev_map(eval_in_env_st.env)lin{stwithhead=f;args=List.rev_appendlst.args;}|T.AppBuiltin(b,l)->(* the arguments in [l] might contain variables *)letty_args,l=List.partitionT.is_a_typelinletarg_tys=List.rev_mapT.ty_exnlinletret_ty=T.ty_exnst.headinletty=T.arrowarg_tysret_tyinletl=List.rev_map(eval_in_env_st.env)lin{stwithhead=T.app_builtin~tybty_args;args=List.rev_appendlst.args;}|_->stletst_of_term~env~tyt={head=t;args=[];env;ty;}|>normalizeletterm_of_stst:T.t=letf=eval_in_env_st.envst.headinT.app~ty:st.tyfst.args(* recursive reduction in call by value. [env] contains the environment for
De Bruijn indexes. *)letrecwhnf_recst=beginmatchT.viewst.head,st.argswith|T.App_,_->assertfalse|T.Var_,_|T.Const_,_->st|T.DB_,_->lett'=eval_in_env_st.envst.headinifT.equalst.headt'thenstelse((* evaluate [db n], then reduce again *){stwithhead=t';env=DBEnv.empty;}|>normalize|>whnf_rec)|T.Bind(Binder.Lambda,ty_var,body),a::args'->(* beta-reduce *)Util.debugf50"(@[<2>beta-reduce@ @[%a@ %a@]@])"(funk->kT.ppst.headT.ppa);assert(not(T.is_groundty_var)||not(T.is_ground(T.ty_exna))||T.equalty_var(T.ty_exna));letst'={head=body;env=DBEnv.pushst.enva;args=args';ty=st.ty;}|>normalizeinwhnf_recst'|T.AppBuiltin_,_|T.Bind_,_->stendletwhnf_term_aux?(env=DBEnv.empty)t=matchT.tytwith|T.HasTypety->letst=st_of_term~ty~envtinletst=whnf_recstinterm_of_stst|_->tletrecwhnf_term?(env=DBEnv.empty)t=ignore(env);letpref,tt=T.open_bindBinder.Lambdatinassert(not(T.is_lambdatt));lethd,args=T.as_appttinifT.is_lambdahd&¬(CCList.is_emptyargs)then(lettt'=whnf_term_auxttinifT.equaltt'ttthentelsewhnf_term(T.fun_lpreftt'))elsetletrecsnf_rect=ifT.is_beta_reducibletthen(lett=whnf_termtinmatchT.tytwith|T.NoType->t|T.HasTypety->beginmatchT.viewtwith|T.App(f,l)->letf'=snf_recfinifnot(T.equalff')thensnf_rec(T.app~tyf'l)else(letl'=List.mapsnf_reclinifT.equalff'&&T.same_lll'thentelseT.app~tyf'l')|T.AppBuiltin(b,l)->letl'=List.mapsnf_reclinifT.same_lll'thentelseT.app_builtin~tybl'|T.Var_|T.Const_|T.DB_->t|T.Bind(b,varty,body)->letbody'=snf_recbodyinifT.equalbodybody'thentelseT.bindb~ty~vartybody'end)elset(* if top_level_only is true, then eta_expand will not recurse *)leteta_expand_rec?(top_level_only=false)t=letrecauxt=matchT.tytwith|T.NoType->t|T.HasTypety->letn,ty_args,ty_ret=T.open_poly_funtyinifn!=0thent(* polymorhpic eta expansion not implemented *)else((* first, WHNF *)lett=whnf_termtin(* see how many arguments are missing, and what type *)letargs,body=T.open_bindBinder.Lambdatinletn_args=List.lengthty_argsinletn_missing=n_args-List.lengthargsinifn_missing>0then(Util.debugf50"(@[eta_expand_rec `%a`,@ missing %d args@ in %a@])"(funk->kT.pptn_missing(CCFormat.Dump.listT.pp)ty_args);(* missing args: suffix of length [n_missing] *)letmissing_args=CCList.drop(n_args-n_missing)ty_argsin(* shift body to accommodate for new binders *)letbody=T.DB.shiftn_missingbodyin(* build the fully-abstracted term *)letdbvars=List.mapi(funity_arg->T.bvar(n_missing-i-1)~ty:ty_arg)missing_argsinT.fun_lty_args(aux(T.app~ty:ty_retbodydbvars)))elseifnottop_level_onlythen(letty=T.ty_exnbodyin(* traverse body *)letbody=matchT.viewbodywith|T.Const_|T.Var_|T.DB_->body|T.App(f,l)->letl'=List.mapauxlinifT.same_lll'thenbodyelseT.app~tyfl'|T.AppBuiltin(b,l)->letl'=List.mapauxlinifT.same_lll'thenbodyelseT.app_builtin~tybl'|T.Bind(b,varty,body')->assert(b<>Binder.Lambda);letbody_reduced=auxbody'inifbody'=body_reducedthenbodyelseT.bind~ty~vartybbody_reducedinT.fun_lty_argsbody)elset)inauxt(* compute eta-reduced normal form *)leteta_reduce_aux?(expand_quant=true)?(full=true)t=letq_reduce~pref_lent=lethd,args=T.as_apptinletn=List.lengthargsinlet_,r_bvars=List.fold_right(funarg(idx,vars)->ifidx=-1then(idx,vars)else(ifidx<pref_len&&T.is_bvar_iidxargthen(idx+1,arg::vars)else(-1,vars)))args(0,[])inletredundant=List.lengthr_bvarsinifredundant=0then0,telse(letnon_redundant=hd::CCList.take(n-redundant)argsinlet_,m=List.fold_right(funarg(idx,m)->ifidx=-1then(idx,m)else(ifnot@@List.exists(funtt->T.DB.containstt(T.as_bvar_exnarg))non_redundantthen(idx+1,m+1)else(-1,m)))r_bvars(0,0)inifm>0then(letargs=CCList.take(n-m)argsinletty=Type.apply_unsafe(Type.of_term_unsafe@@T.ty_exnhd)argsinm,T.DB.unshiftm(T.app~ty:(ty:>T.t)hdargs))else0,t)inletrecauxt=ifT.is_eta_reducibletthen(matchT.tytwith|T.NoType->t|T.HasTypety->beginmatchT.viewtwith|T.Var_|T.DB_|T.Const_->t|T.Bind(Binder.Lambda,_,_)->letpref,body=T.open_bindBinder.Lambdatinletbody'=iffullthenauxbodyelsebodyinletn,reduced=q_reduce~pref_len:(List.lengthpref)body'inassert(Type.equal(Type.of_term_unsafe@@T.ty_exnbody)(Type.of_term_unsafe@@T.ty_exnbody'));ifn=0&&T.equalbodybody'thentelse(T.fun_l(CCList.take(List.lengthpref-n)pref)reduced)|T.Bind(_,_,_)->t|T.App(_,[])->assertfalse|T.App(f,l)->letf'=auxfinletl'=List.mapauxlinifT.equalff'&&T.same_lll'thentelseT.app~tyf'l'|T.AppBuiltin(Builtin.(ExistsConst|ForallConst)ashd,[tyarg;body])whenexpand_quant->(* top-level eta expand body of the quantifier *)letbody'=eta_expand_rec~top_level_only:truebodyinletpref,matrix=T.open_bindBinder.Lambdabody'in(* reduce everything underneath the body *)letmatrix'=auxmatrixinletbody'=T.fun_lprefmatrix'inifT.equalbody'bodythentelseT.app_builtin~ty:(T.ty_exnt)hd[tyarg;body']|T.AppBuiltin(b,l)->letl'=List.mapauxlinifT.same_lll'thentelseT.app_builtin~tybl'end)elsetinlett'=auxtint'letwhnft=lett'=ZProf.with_profprof_whnfwhnf_termtint'letbeta_red_headt=letpref,body=T.open_funtinletres=T.fun_lpref(whnfbody)inresletadd_args_tail~tystargs:state={stwithargs=st.args@args;ty;}letsnft=lett'=ZProf.with_profprof_snfsnf_rectint'leteta_expandt=ZProf.with_profprof_eta_expandeta_expand_rectleteta_reduce?(expand_quant=true)?(full=true)t=ZProf.with_profprof_eta_reduce(eta_reduce_aux~expand_quant~full)tendmoduleT=TermmoduleIT=InnerTermtypeterm=Term.tletwhnft=Inner.whnf(t:T.t:>IT.t)|>T.of_term_unsafeletwhnf_listtargs=letst=Inner.st_of_term~env:DBEnv.empty~ty:(T.tyt:Type.t:>IT.t)(t:T.t:>IT.t)inletty=Type.apply_unsafe(T.tyt)(args:T.tlist:>IT.tlist)inletst=Inner.add_args_tailst(args:T.tlist:>IT.tlist)~ty:(ty:Type.t:>IT.t)inletst=Inner.whnf_recstinlett'=Inner.term_of_stst|>T.of_term_unsafeint'letsnft=Inner.snf_rec(t:T.t:>IT.t)|>T.of_term_unsafeleteta_expandt=Inner.eta_expand(t:T.t:>IT.t)|>T.of_term_unsafe(*|> CCFun.tap (fun t' ->
if t != t' then Format.printf "@[eta_expand `%a`@ into `%a`@]@." T.pp t T.pp t')*)leteta_reduce?(expand_quant=true)?(full=true)t=Inner.eta_reduce~full~expand_quant(t:T.t:>IT.t)|>T.of_term_unsafe(*|> CCFun.tap (fun t' ->
if t != t' then Format.printf "@[eta_reduce `%a`@ into `%a`@]@." T.pp t T.pp t')*)letbeta_red_headt=Inner.beta_red_head(t:T.t:>IT.t)|>T.of_term_unsafeletrecis_lambda_patternt=matchT.view(whnft)with|T.AppBuiltin(_,ts)->List.for_allis_lambda_patternts|T.DB_|T.Var_|T.Const_->true|T.App(hd,args)->ifT.is_varhdthenall_distinct_boundargselseList.for_allis_lambda_patternargs|T.Fun(_,body)->is_lambda_patternbodyandall_distinct_boundargs=tryList.map(funarg->matchT.view(eta_reducearg)with|T.DBi->i|_->raiseExit)args|>Util.Int_set.of_list|>(funset->Util.Int_set.cardinalset=List.lengthargs)withExit->false