123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390(************************************************************************)(* * The Coq Proof Assistant / The Coq Development Team *)(* v * Copyright INRIA, CNRS and contributors *)(* <O___,, * (see version control and CREDITS file for authors & dates) *)(* \VV/ **************************************************************)(* // * This file is distributed under the terms of the *)(* * GNU Lesser General Public License Version 2.1 *)(* * (see LICENSE file for the text of the license) *)(************************************************************************)(** This file defines the low-level monadic operations used by the
tactic monad. The monad is divided into two layers: a non-logical
layer which consists in operations which will not (or cannot) be
backtracked in case of failure (input/output or persistent state)
and a logical layer which handles backtracking, proof
manipulation, and any other effect which needs to backtrack. *)(** {6 Exceptions} *)(** To help distinguish between exceptions raised by the IO monad from
the one used natively by Coq, the former are wrapped in
[Exception]. It is only used internally so that [catch] blocks of
the IO monad would only catch exceptions raised by the [raise]
function of the IO monad, and not for instance, by system
interrupts. Also used in [Proofview] to avoid capturing exception
from the IO monad ([Proofview] catches errors in its compatibility
layer, and when lifting goal-level expressions). *)exceptionExceptionofexn(** This exception is used to signal abortion in [timeout] functions. *)exceptionTac_Timeout(** This exception is used by the tactics to signal failure by lack of
successes, rather than some other exceptions (like system
interrupts). *)exceptionTacticFailureofexnlet_=CErrors.register_handlerbeginfunction|Exceptione->Some(CErrors.printe)|TacticFailuree->Some(CErrors.printe)|_->Noneend(** {6 Non-logical layer} *)(** The non-logical monad is a simple [unit -> 'a] (i/o) monad. The
operations are simple wrappers around corresponding usual
operations and require little documentation. *)moduleNonLogical=struct(* The functions in this module follow the pattern that they are
defined with the form [(); fun ()->...]. This is an optimisation
which signals to the compiler that the function is usually partially
applied up to the [();]. Without this annotation, partial
applications can be significantly slower.
Documentation of this behaviour can be found at:
https://blog.janestreet.com/the-dangers-of-being-too-partial/ *)includeMonad.Make(structtype'at=unit->'aletreturna=();fun()->alet(>>=)ak=();fun()->k(a())()let(>>)ak=();fun()->a();k()letmapfa=();fun()->f(a())end)type'aref='aStdlib.refletignorea=();fun()->ignore(a())letrefa=();fun()->refa(** [Pervasives.(:=)] *)let(:=)ra=();fun()->r:=a(** [Pervasives.(!)] *)let(!)=funr->();fun()->!r(** [Pervasives.raise]. Except that exceptions are wrapped with
{!Exception}. *)letraise(e,info)()=Exninfo.iraise(Exceptione,info)(** [try ... with ...] but restricted to {!Exception}. *)letcatch=funsh->();fun()->trys()withExceptioneassrc->let(src,info)=Exninfo.capturesrcinh(e,info)()letread_line=fun()->tryread_line()withe->let(e,info)=Exninfo.captureeinraise(e,info)()letprint_char=func->();fun()->print_charclettimeout=funnt->();fun()->Control.timeoutnt()letmakef=();fun()->tryf()withewhenCErrors.noncriticale->let(e,info)=Exninfo.captureeinExninfo.iraise(Exceptione,info)(** Use the current logger. The buffer is also flushed. *)letprint_debugs=make(fun_->Feedback.msg_debugs)letprint_infos=make(fun_->Feedback.msg_infos)letprint_warnings=make(fun_->Feedback.msg_warnings)letprint_notices=make(fun_->Feedback.msg_notices)letrun=funx->tryx()withExceptioneassrc->let(src,info)=Exninfo.capturesrcinExninfo.iraise(e,info)end(** {6 Logical layer} *)(** The logical monad is a backtracking monad on top of which is
layered a state monad (which is used to implement all of read/write,
read only, and write only effects). The state monad being layered on
top of the backtracking monad makes it so that the state is
backtracked on failure.
Backtracking differs from regular exception in that, writing (+)
for exception catching and (>>=) for bind, we require the
following extra distributivity laws:
x+(y+z) = (x+y)+z
zero+x = x
x+zero = x
(x+y)>>=k = (x>>=k)+(y>>=k) *)(** A view type for the logical monad, which is a form of list, hence
we can decompose it with as a list. *)type('a,'b,'e)list_view_=|Nilof'e|Consof'a*'btype('a,'b,'e)list_view=('a,'e->'b,'e)list_view_moduleBackState=struct(** Double-continuation backtracking monads are reasonable folklore
for "search" implementations (including the Tac interactive
prover's tactics). Yet it's quite hard to wrap your head around
these. I recommend reading a few times the "Backtracking,
Interleaving, and Terminating Monad Transformers" paper by
O. Kiselyov, C. Shan, D. Friedman, and A. Sabry. The peculiar
shape of the monadic type is reminiscent of that of the
continuation monad transformer.
The paper also contains the rationale for the [split] abstraction.
An explanation of how to derive such a monad from mathematical
principles can be found in "Kan Extensions for Program
Optimisation" by Ralf Hinze.
A somewhat concrete view is that the type ['a iolist] is, in fact
the impredicative encoding of the following stream type:
[type 'a _iolist' = Nil of exn | Cons of 'a*'a iolist'
and 'a iolist = 'a _iolist NonLogical.t]
Using impredicative encoding avoids intermediate allocation and
is, empirically, very efficient in Ocaml. It also has the
practical benefit that the monadic operation are independent of
the underlying monad, which simplifies the code and side-steps
the limited inlining of Ocaml.
In that vision, [bind] is simply [concat_map] (though the cps
version is significantly simpler), [plus] is concatenation, and
[split] is pattern-matching. *)type('a,'i,'o,'e)t={iolist:'r.'i->('e->'rNonLogical.t)->('a->'o->('e->'rNonLogical.t)->'rNonLogical.t)->'rNonLogical.t}letreturnx={iolist=funsnilcons->consxsnil}let(>>=)mf={iolist=funsnilcons->m.iolistsnil(funxsnext->(fx).iolistsnextcons)}let(>>)mf={iolist=funsnilcons->m.iolistsnil(fun()snext->f.iolistsnextcons)}letmapfm={iolist=funsnilcons->m.iolistsnil(funxsnext->cons(fx)snext)}letzeroe={iolist=fun_nilcons->nile}letplusm1m2={iolist=funsnilcons->m1.iolists(fune->(m2e).iolistsnilcons)cons}letignorem={iolist=funsnilcons->m.iolistsnil(fun_snext->cons()snext)}letliftm={iolist=funsnilcons->NonLogical.(m>>=funx->consxsnil)}(** State related *)letget={iolist=funsnilcons->consssnil}letsets={iolist=fun_nilcons->cons()snil}letmodifyf={iolist=funsnilcons->cons()(fs)nil}(** Exception manipulation *)letinterleavesrcdstm={iolist=funsnilcons->m.iolists(fune1->nil(srce1))(funxsnext->consxs(fune2->next(dste2)))}(** List observation *)letoncem={iolist=funsnilcons->m.iolistsnil(funxs_->consxsnil)}letbreakfm={iolist=funsnilcons->m.iolistsnil(funxsnext->consxs(fune->matchfewithNone->nexte|Somee->nile))}(** For [reflect] and [split] see the "Backtracking, Interleaving,
and Terminating Monad Transformers" paper. *)type('a,'e)reified=('a,('a,'e)reified_,'e)list_view_NonLogical.tand('a,'e)reified_={r:'e->('a,'e)reified}[@@unboxed]letrecreflect(m:('a*'o,'e)reified)={iolist=funs0nilcons->letnext=function|Nile->nile|Cons((x,s),{r=l})->consxs(fune->(reflect(le)).iolists0nilcons)inNonLogical.(m>>=next)}letsplitm:(_list_view,_,_,_)t=letrnile=NonLogical.return(Nile)inletrconspsl=NonLogical.return(Cons((p,s),{r=l}))in{iolist=funsnilcons->letopenNonLogicalinm.iolistsrnilrcons>>=beginfunction|Nile->cons(Nile)snil|Cons((x,s),{r=l})->letle=reflect(le)incons(Cons(x,l))snilend}letrunms=letrnile=NonLogical.return(Nile)inletrconsxsl=letp=(x,s)inNonLogical.return(Cons(p,{r=l}))inm.iolistsrnilrconsletreprx=xendmoduletypeParam=sig(** Read only *)typee(** Write only *)typew(** [w] must be a monoid *)valwunit:wvalwprod:w->w->w(** Read-write *)types(** Update-only. Essentially a writer on [u->u]. *)typeu(** [u] must be pointed. *)valuunit:uendmoduleLogical(P:Param)=structmoduleUnsafe=struct(** All three of environment, writer and state are coded as a single
state-passing-style monad.*)typestate={rstate:P.e;ustate:P.u;wstate:P.w;sstate:P.s;}letmakem=mletreprm=mendopenUnsafetypestate=Unsafe.statetypeiexn=Exninfo.iexntype'areified=('a,iexn)BackState.reifiedtype'areified_=('a,iexn)BackState.reified_(** Inherited from Backstate *)openBackStateincludeMonad.Make(structtype'at=('a,state,state,iexn)BackState.tletreturn=BackState.returnlet(>>=)=BackState.(>>=)let(>>)=BackState.(>>)letmap=BackState.mapend)letzero=BackState.zeroletplus=BackState.plusletignore=BackState.ignoreletlift=BackState.liftletonce=BackState.onceletbreak=BackState.breakletsplit=BackState.splitletrepr=BackState.repr(** State related. We specialize them here to ensure soundness (for reader and
writer) and efficiency. *)letget={iolist=funsnilcons->conss.sstatesnil}letset(sstate:P.s)={iolist=funsnilcons->cons(){swithsstate}nil}letmodify(f:P.s->P.s)={iolist=funsnilcons->cons(){swithsstate=fs.sstate}nil}letcurrent={iolist=funsnilcons->conss.rstatesnil}letlocalem={iolist=funsnilcons->m.iolist{swithrstate=e}nil(funxs'next->consx{s'withrstate=s.rstate}next)}letputw={iolist=funsnilcons->cons(){swithwstate=P.wprods.wstatew}nil}letupdate(f:P.u->P.u)={iolist=funsnilcons->cons(){swithustate=fs.ustate}nil}(** Monadic run is specialized to handle reader / writer *)letrunmrs=lets={wstate=P.wunit;ustate=P.uunit;rstate=r;sstate=s}inletrnile=NonLogical.return(Nile)inletrconsxsl=letp=(x,s.sstate,s.wstate,s.ustate)inNonLogical.return(Cons(p,{r=l}))inm.iolistsrnilrconsend