{-# LANGUAGE PatternGuards, KindSignatures #-}{-# LANGUAGE ExistentialQuantification, RankNTypes, ImpredicativeTypes #-}-- This file is the CPS version of CCExc.hs, implementing the identical-- interface---- <http://okmij.org/ftp/continuations/implementations.html#CC-monads>---- Monad transformer for multi-prompt delimited control-- It implements the superset of the interface described in---- > A Monadic Framework for Delimited Continuations-- > R. Kent Dybvig, Simon Peyton Jones, and Amr Sabry-- > JFP, v17, N6, pp. 687--730, 2007.-- > http://www.cs.indiana.edu/cgi-bin/techreports/TRNNN.cgi?trnum=TR615---- The first main difference is the use of generalized prompts, which-- do not have to be created with new_prompt and therefore can be defined-- at top level. That removes one of the main practical drawbacks of-- Dybvig et al implementations: the necessity to carry around the prompts-- throughout all the code.---- The delimited continuation monad is parameterized by the flavor-- of generalized prompts. The end of this code defines several flavors;-- the library users may define their own. User-defined flavors are -- especially useful when user's code uses a small closed set of answer-types. -- Flavors PP and PD below are more general, assuming the set of possible-- answer-types is open and Typeable. If the user wishes to create several-- distinct prompts with the same answer-types, the user should use-- the flavor of prompts accepting an integral prompt identifier, such as PD.-- Prompts of the flavor PD correspond to the prompts in Dybvig, Peyton Jones,-- Sabry framework. If the user wishes to generate unique prompts, the user-- should arrange himself for the generation of unique integers-- (using a state monad, for example). On the other hand, the user-- can differentiate answer-types using `newtype.' The latter can-- only produce the set of distinct prompts that is fixed at run-time.-- Sometimes that is sufficient. There is not need to create a gensym-- monad then.---- See CCExc.hs for further comments about the implementationmoduleControl.CCCxe(CC,-- TypesSubCont,CCT,Prompt,-- Basic delimited control operationspushPrompt,takeSubCont,pushSubCont,runCC,-- Useful derived operationsabortP,shiftP,shift0P,controlP,-- Pre-defined prompt flavorsPS,ps,P2,p2L,p2R,PP,pp,PM,pm,PD,newPrompt,as_prompt_type)whereimportControl.Monad.TransimportData.Typeable-- for prompts of the flavor PP, PD-- | Delimited-continuation monad transformer-- It is parameterized by the prompt flavor p-- The first argument is the regular (success) continuation,-- the second argument is the bubble, or a resumable exceptionnewtypeCCpma=CC{unCC::forallw.(a->mw)->(forallx.SubContpmxa->pmx->mw)->mw}-- | The captured sub-continuationtypeSubContpmab=CCpma->CCpmb-- | The type of control operator's bodytypeCCTpmaw=SubContpmaw->CCpmw-- | Generalized prompts for the answer-type w: an injection-projection pairtypePromptpmw=(forallx.CCTpmxw->pmx,forallx.pmx->Maybe(CCTpmxw))-- ---------------------------------------------------------------------- | CC monad: general monadic operations--instanceMonadm=>Monad(CCpm)wherereturnx=CC$\kikd->kixm>>=f=CC$\kikd->unCCm(\a->unCC(fa)kikd)(\ctx->kd(\x->ctxx>>=f))instanceMonadTrans(CCp)whereliftm=CC$\kikd->m>>=kiinstanceMonadIOm=>MonadIO(CCpm)whereliftIO=lift.liftIO-- ---------------------------------------------------------------------- | Basic Operations of the delimited control interface--pushPrompt::Monadm=>Promptpmw->CCpmw->CCpmwpushPromptp@(_,proj)body=CC$\kikd->letkd'ctxbody|Justb<-projbody=unCC(bctx)kikdkd'ctxbody=kd(\x->pushPromptp(ctxx))bodyinunCCbodykikd'-- | Create the initial bubbletakeSubCont::Monadm=>Promptpmw->CCTpmxw->CCpmxtakeSubContp@(inj,_)body=CC$\kikd->kdid(injbody)-- | Apply the captured continuationpushSubCont::Monadm=>SubContpmab->CCpma->CCpmbpushSubCont=($)runCC::Monadm=>CC(p::(*->*)->*->*)ma->marunCCm=unCCmreturnerrwhereerr=error"Escaping bubble: you have forgotten pushPrompt"-- ---------------------------------------------------------------------- | Useful derived operations--abortP::Monadm=>Promptpmw->CCpmw->CCpmanyabortPpe=takeSubContp(\_->e)shiftP::Monadm=>Promptpmw->((a->CCpmw)->CCpmw)->CCpmashiftPpf=takeSubContp$\sk->pushPromptp(f(\c->pushPromptp(pushSubContsk(returnc))))shift0P::Monadm=>Promptpmw->((a->CCpmw)->CCpmw)->CCpmashift0Ppf=takeSubContp$\sk->f(\c->pushPromptp(pushSubContsk(returnc)))controlP::Monadm=>Promptpmw->((a->CCpmw)->CCpmw)->CCpmacontrolPpf=takeSubContp$\sk->pushPromptp(f(\c->pushSubContsk(returnc)))-- ---------------------------------------------------------------------- Prompt flavors-- | The extreme case: prompts for the single answer-type w.-- The monad (CC PS) then is the monad for regular (single-prompt) -- delimited continuationsnewtypePSwmx=PS(CCT(PSw)mxw)-- | There is only one generalized prompt of the flavor PS for a-- given answer-type w. It is defined belowps::Prompt(PSw)mwps=(inj,prj)whereinj=PSprj(PSx)=Justx-- | Prompts for the closed set of answer-types-- The following prompt flavor P2, for two answer-types w1 and w2,-- is given as an example. Typically, a programmer would define their-- own variant data type with variants for the answer-types that occur-- in their program.--newtypeP2w1w2mx=P2(Either(CCT(P2w1w2)mxw1)(CCT(P2w1w2)mxw2))-- | There are two generalized prompts of the flavor P2p2L::Prompt(P2w1w2)mw1p2L=(inj,prj)whereinj=P2.Leftprj(P2(Leftx))=Justxprj_=Nothingp2R::Prompt(P2w1w2)mw2p2R=(inj,prj)whereinj=P2.Rightprj(P2(Rightx))=Justxprj_=Nothing-- | Prompts for the open set of answer-types--dataPPmx=forallw.Typeablew=>PP(CCTPPmxw)-- | We need to wrap the type alias CCT into a newtype. Otherwise, gcast-- doesn't work. We can't treat (CCT p m a w) as a an application of-- the `type constructor' (CCT p m a) to the type w: type aliases can't -- be partially applied. But we can treat the type (NCCT p m a w) that way.newtypeNCCTpmaw=NCCT{unNCCT::CCTpmaw}pp::Typeablew=>PromptPPmwpp=(inj,prj)whereinj=PPprj(PPc)=maybeNothing(Just.unNCCT)(gcast(NCCTc))-- | The same as PP but with the phantom parameter c-- The parameter is useful to statically enforce various constrains-- (statically pass some information between shift and reset)-- The prompt PP is too `dynamic': all errors are detected dynamically-- See Generator2.hs for an exampledataPMcmx=forallw.Typeablew=>PM(CCT(PMc)mxw)pm::Typeablew=>Prompt(PMc)mwpm=(inj,prj)whereinj=PMprj(PMc)=maybeNothing(Just.unNCCT)(gcast(NCCTc))-- | Open set of answer types, with an additional distinction (given by-- integer identifiers)-- This prompt flavor corresponds to the prompts in the Dybvig, Peyton-Jones,-- Sabry framework (modulo the Typeable constraint).--dataPDmx=forallw.Typeablew=>PDInt(CCTPDmxw)newPrompt::Typeablew=>Int->PromptPDmwnewPromptmark=(inj,prj)whereinj=PDmarkprj(PDmark'c)|mark'==mark,Just(NCCTx)<-gcast(NCCTc)=Justxprj_=Nothing-- | It is often helpful, for clarity of error messages, to specify the -- answer-type associated with the prompt explicitly (rather than relying -- on the type inference to figure that out). The following function-- is useful for that purpose.as_prompt_type::Promptpmw->w->Promptpmwas_prompt_type=const