{-# OPTIONS_HADDOCK not-home #-}{-# LANGUAGE FlexibleInstances #-}{-# LANGUAGE MultiParamTypeClasses #-}{-# LANGUAGE UndecidableInstances #-}{-# LANGUAGE RankNTypes #-}{-# LANGUAGE TupleSections #-}moduleData.Conduit.Internal(-- * TypesPipe(..),Source,GSource,Sink,GSink,GLSink,GInfSink,GLInfSink,Conduit,GConduit,GLConduit,GInfConduit,GLInfConduit,ResumableSource(..)-- * Primitives,await,awaitE,awaitForever,yield,yieldOr,leftover-- * Finalization,bracketP,addCleanup-- * Composition,idP,pipe,pipeL,connectResume,runPipe,injectLeftovers-- * Generalizing,sourceToPipe,sinkToPipe,conduitToPipe-- * Utilities,transPipe,mapOutput,mapOutputMaybe,mapInput,sourceList,withUpstream,unwrapResumable)whereimportControl.Applicative(Applicative(..))importControl.Monad((>=>),liftM,ap,when)importControl.Monad.Trans.Class(MonadTrans(lift))importControl.Monad.IO.Class(MonadIO(liftIO))importControl.Monad.Base(MonadBase(liftBase))importData.Void(Void,absurd)importData.Monoid(Monoid(mappend,mempty))importControl.Monad.Trans.ResourceimportqualifiedGHC.ExtsimportqualifiedData.IORefasI-- | The underlying datatype for all the types in this package. In has six-- type parameters:---- * /l/ is the type of values that may be left over from this @Pipe@. A @Pipe@-- with no leftovers would use @Void@ here, and one with leftovers would use-- the same type as the /i/ parameter. Leftovers are automatically provided to-- the next @Pipe@ in the monadic chain.---- * /i/ is the type of values for this @Pipe@'s input stream.---- * /o/ is the type of values for this @Pipe@'s output stream.---- * /u/ is the result type from the upstream @Pipe@.---- * /m/ is the underlying monad.---- * /r/ is the result type.---- A basic intuition is that every @Pipe@ produces a stream of output values-- (/o/), and eventually indicates that this stream is terminated by sending a-- result (/r/). On the receiving end of a @Pipe@, these become the /i/ and /u/-- parameters.---- Since 0.5.0dataPipelioumr=-- | Provide new output to be sent downstream. This constructor has three-- fields: the next @Pipe@ to be used, a finalization function, and the-- output value.HaveOutput(Pipelioumr)(m())o-- | Request more input from upstream. The first field takes a new input-- value and provides a new @Pipe@. The second takes an upstream result-- value, which indicates that upstream is producing no more results.|NeedInput(i->Pipelioumr)(u->Pipelioumr)-- | Processing with this @Pipe@ is complete, providing the final result.|Doner-- | Require running of a monadic action to get the next @Pipe@.|PipeM(m(Pipelioumr))-- | Return leftover input, which should be provided to future operations.|Leftover(Pipelioumr)linstanceMonadm=>Functor(Pipelioum)wherefmap=liftMinstanceMonadm=>Applicative(Pipelioum)wherepure=return(<*>)=apinstanceMonadm=>Monad(Pipelioum)wherereturn=DoneDonex>>=fp=fpxHaveOutputpco>>=fp=HaveOutput(p>>=fp)coNeedInputpc>>=fp=NeedInput(p>=>fp)(c>=>fp)PipeMmp>>=fp=PipeM((>>=fp)`liftM`mp)Leftoverpi>>=fp=Leftover(p>>=fp)iinstanceMonadBasebasem=>MonadBasebase(Pipelioum)whereliftBase=lift.liftBaseinstanceMonadTrans(Pipeliou)whereliftmr=PipeM(Done`liftM`mr)instanceMonadIOm=>MonadIO(Pipelioum)whereliftIO=lift.liftIOinstanceMonadm=>Monoid(Pipelioum())wheremempty=return()mappend=(>>)-- | Provides a stream of output values, without consuming any input or-- producing a final result.---- Since 0.5.0typeSourcemo=Pipe()()o()m()-- | Generalized 'Source'.---- Since 0.5.0typeGSourcemo=forallliu.Pipelioum()-- | Consumes a stream of input values and produces a final result, without-- producing any output.---- Since 0.5.0typeSinkimr=PipeiiVoid()mr-- | Generalized 'Sink' without leftovers.---- Since 0.5.0typeGSinkimr=foralllou.Pipelioumr-- | Generalized 'Sink' with leftovers.---- Since 0.5.0typeGLSinkimr=forallou.Pipeiioumr-- | Generalized 'Sink' without leftovers returning upstream result.---- Since 0.5.0typeGInfSinkim=foralllor.Pipeliormr-- | Generalized 'Sink' with leftovers returning upstream result.---- Since 0.5.0typeGLInfSinkim=forallor.Pipeiiormr-- | Consumes a stream of input values and produces a stream of output values,-- without producing a final result.---- Since 0.5.0typeConduitimo=Pipeiio()m()-- | Generalized conduit without leftovers.---- Since 0.5.0typeGConduitimo=foralllu.Pipelioum()-- | Generalized conduit with leftovers.---- Since 0.5.0typeGLConduitimo=forallu.Pipeiioum()-- | Generalized conduit without leftovers returning upstream result.---- Since 0.5.0typeGInfConduitimo=foralllr.Pipeliormr-- | Generalized conduit with leftovers returning upstream result.---- Since 0.5.0typeGLInfConduitimo=forallr.Pipeiiormr-- | A @Source@ which has been started, but has not yet completed.---- This type contains both the current state of the @Source@, and the finalizer-- to be run to close it.---- Since 0.5.0dataResumableSourcemo=ResumableSource(Sourcemo)(m())-- | Wait for a single input value from upstream, terminating immediately if no-- data is available.---- Since 0.5.0await::Pipelioum(Maybei)await=NeedInput(Done.Just)(\_->DoneNothing){-# RULES "await >>= maybe" forall x y. await >>= maybe x y = NeedInput y (const x) #-}{-# INLINE [1] await #-}-- | This is similar to @await@, but will return the upstream result value as-- @Left@ if available.---- Since 0.5.0awaitE::Pipelioum(Eitherui)awaitE=NeedInput(Done.Right)(Done.Left){-# RULES "awaitE >>= either" forall x y. awaitE >>= either x y = NeedInput y x #-}{-# INLINE [1] awaitE #-}-- | Wait for input forever, calling the given inner @Pipe@ for each piece of-- new input. Returns the upstream result type.---- Since 0.5.0awaitForever::Monadm=>(i->Pipeliormr')->PipeliormrawaitForeverinner=selfwhereself=awaitE>>=eitherreturn(\i->inneri>>self){-# INLINE [1] awaitForever #-}-- | Send a single output value downstream. If the downstream @Pipe@-- terminates, this @Pipe@ will terminate as well.---- Since 0.5.0yield::Monadm=>o-- ^ output value->Pipelioum()yield=HaveOutput(Done())(return()){-# INLINE [1] yield #-}-- | Similar to @yield@, but additionally takes a finalizer to be run if the-- downstream @Pipe@ terminates.---- Since 0.5.0yieldOr::Monadm=>o->m()-- ^ finalizer->Pipelioum()yieldOrof=HaveOutput(Done())fo{-# INLINE [1] yieldOr #-}{-# RULES
"yield o >> p" forall o (p :: Pipe l i o u m r). yield o >> p = HaveOutput p (return ()) o
; "mapM_ yield" mapM_ yield = sourceList
; "yieldOr o c >> p" forall o c (p :: Pipe l i o u m r). yieldOr o c >> p = HaveOutput p c o
#-}-- | Provide a single piece of leftover input to be consumed by the next pipe-- in the current monadic binding.---- /Note/: it is highly encouraged to only return leftover values from input-- already consumed from upstream.---- Since 0.5.0leftover::l->Pipelioum()leftover=Leftover(Done()){-# INLINE [1] leftover #-}{-# RULES "leftover l >> p" forall l (p :: Pipe l i o u m r). leftover l >> p = Leftover p l #-}-- | Perform some allocation and run an inner @Pipe@. Two guarantees are given-- about resource finalization:---- 1. It will be /prompt/. The finalization will be run as early as possible.---- 2. It is exception safe. Due to usage of @resourcet@, the finalization will-- be run in the event of any exceptions.---- Since 0.5.0bracketP::MonadResourcem=>IOa->(a->IO())->(a->Pipelioumr)->PipelioumrbracketPallocfreeinside=PipeMstartwherestart=do(key,seed)<-allocateallocfreereturn$addCleanup(const$releasekey)(insideseed)-- | Add some code to be run when the given @Pipe@ cleans up.---- Since 0.4.1addCleanup::Monadm=>(Bool->m())-- ^ @True@ if @Pipe@ ran to completion, @False@ for early termination.->Pipelioumr->PipelioumraddCleanupcleanup(Doner)=PipeM(cleanupTrue>>return(Doner))addCleanupcleanup(HaveOutputsrcclosex)=HaveOutput(addCleanupcleanupsrc)(cleanupFalse>>close)xaddCleanupcleanup(PipeMmsrc)=PipeM(liftM(addCleanupcleanup)msrc)addCleanupcleanup(NeedInputpc)=NeedInput(addCleanupcleanup.p)(addCleanupcleanup.c)addCleanupcleanup(Leftoverpi)=Leftover(addCleanupcleanupp)i-- | The identity @Pipe@.---- Since 0.5.0idP::Monadm=>PipelaarmridP=NeedInput(HaveOutputidP(return()))Done-- | Compose a left and right pipe together into a complete pipe. The left pipe-- will be automatically closed when the right pipe finishes.---- Since 0.5.0pipe::Monadm=>Pipelabr0mr1->PipeVoidbcr1mr2->Pipelacr0mr2pipe=pipe'(return())wherepipe'finalleftright=caserightofDoner2->PipeM(final>>return(Doner2))HaveOutputpco->HaveOutput(pipe'finalleftp)coPipeMmp->PipeM(liftM(pipe'finalleft)mp)Leftover_i->absurdiNeedInputrprc->upstreamrprcwhereupstreamrprc=caseleftofDoner1->pipe(Doner1)(rcr1)HaveOutputleft'final'o->pipe'final'left'(rpo)PipeMmp->PipeM(liftM(\left'->pipe'finalleft'right)mp)Leftoverleft'i->Leftover(pipe'finalleft'right)iNeedInputleft'lc->NeedInput(\a->pipe'final(left'a)right)(\r0->pipe'final(lcr0)right)-- | Same as 'pipe', but automatically applies 'injectLeftovers' to the right @Pipe@.---- Since 0.5.0pipeL::Monadm=>Pipelabr0mr1->Pipebbcr1mr2->Pipelacr0mr2-- Note: The following should be equivalent to the simpler:---- pipeL l r = l `pipe` injectLeftovers r---- However, this version tested as being significantly more efficient.pipeL=pipe'(return())wherepipe'::Monadm=>m()->Pipelabr0mr1->Pipebbcr1mr2->Pipelacr0mr2pipe'finalleftright=caserightofDoner2->PipeM(final>>return(Doner2))HaveOutputpco->HaveOutput(pipe'finalleftp)coPipeMmp->PipeM(liftM(pipe'finalleft)mp)Leftoverright'i->pipe'final(HaveOutputleftfinali)right'NeedInputrprc->caseleftofDoner1->pipe'(return())(Doner1)(rcr1)HaveOutputleft'final'o->pipe'final'left'(rpo)PipeMmp->PipeM(liftM(\left'->pipe'finalleft'right)mp)NeedInputleft'lc->NeedInput(\a->pipe'final(left'a)right)(\r0->pipe'final(lcr0)right)Leftoverleft'i->Leftover(pipe'finalleft'right)i-- | Connect a @Source@ to a @Sink@ until the latter closes. Returns both the-- most recent state of the @Source@ and the result of the @Sink@.---- We use a @ResumableSource@ to keep track of the most recent finalizer-- provided by the @Source@.---- Since 0.5.0connectResume::Monadm=>ResumableSourcemo->Sinkomr->m(ResumableSourcemo,r)connectResume(ResumableSourceleft0leftFinal0)=goleftFinal0left0wheregoleftFinalleftright=caserightofDoner2->return(ResumableSourceleftleftFinal,r2)PipeMmp->mp>>=goleftFinalleftHaveOutput__o->absurdoLeftoverpi->goleftFinal(HaveOutputleftleftFinali)pNeedInputrprc->caseleftofLeftoverp()->goleftFinalprightHaveOutputleft'leftFinal'o->goleftFinal'left'(rpo)NeedInput_lc->goleftFinal(lc())rightDone()->go(return())(Done())(rc())PipeMmp->mp>>=\left'->goleftFinalleft'right-- | Run a pipeline until processing completes.---- Since 0.5.0runPipe::Monadm=>PipeVoid()Void()mr->mrrunPipe(HaveOutput__o)=absurdorunPipe(NeedInput_c)=runPipe(c())runPipe(Doner)=returnrrunPipe(PipeMmp)=mp>>=runPiperunPipe(Leftover_i)=absurdi-- | Transforms a @Pipe@ that provides leftovers to one which does not,-- allowing it to be composed.---- This function will provide any leftover values within this @Pipe@ to any-- calls to @await@. If there are more leftover values than are demanded, the-- remainder are discarded.---- Since 0.5.0injectLeftovers::Monadm=>Pipeiioumr->PipelioumrinjectLeftovers(Doner)=DonerinjectLeftovers(PipeMmp)=PipeM(liftMinjectLeftoversmp)injectLeftovers(NeedInputpc)=NeedInput(injectLeftovers.p)(injectLeftovers.c)injectLeftovers(HaveOutputpco)=HaveOutput(injectLeftoversp)coinjectLeftovers(Leftoverp0i0)=injectLeftovers$injecti0p0whereinject_(Doner)=Donerinjecti(NeedInputp_)=piinjecti(PipeMmp)=PipeM$liftM(injecti)mpinjecti(HaveOutputpco)=HaveOutput(injectip)coinjecti(Leftoverpi')=caseinjecti'pofLeftoverp'_->p'p'->Leftoverp'i-- | Transform the monad that a @Pipe@ lives in.---- Since 0.4.0transPipe::Monadm=>(foralla.ma->na)->Pipelioumr->PipeliounrtransPipef(HaveOutputpco)=HaveOutput(transPipefp)(fc)otransPipef(NeedInputpc)=NeedInput(transPipef.p)(transPipef.c)transPipe_(Doner)=DonertransPipef(PipeMmp)=PipeM(f$liftM(transPipef)mp)transPipef(Leftoverpi)=Leftover(transPipefp)i-- | Apply a function to all the output values of a @Pipe@.---- This mimics the behavior of `fmap` for a `Source` and `Conduit` in pre-0.4-- days.---- Since 0.4.1mapOutput::Monadm=>(o1->o2)->Pipelio1umr->Pipelio2umrmapOutputf(HaveOutputpco)=HaveOutput(mapOutputfp)c(fo)mapOutputf(NeedInputpc)=NeedInput(mapOutputf.p)(mapOutputf.c)mapOutput_(Doner)=DonermapOutputf(PipeMmp)=PipeM(liftM(mapOutputf)mp)mapOutputf(Leftoverpi)=Leftover(mapOutputfp)i-- | Same as 'mapOutput', but use a function that returns @Maybe@ values.---- Since 0.5.0mapOutputMaybe::Monadm=>(o1->Maybeo2)->Pipelio1umr->Pipelio2umrmapOutputMaybef(HaveOutputpco)=maybeid(\o'p'->HaveOutputp'co')(fo)(mapOutputMaybefp)mapOutputMaybef(NeedInputpc)=NeedInput(mapOutputMaybef.p)(mapOutputMaybef.c)mapOutputMaybe_(Doner)=DonermapOutputMaybef(PipeMmp)=PipeM(liftM(mapOutputMaybef)mp)mapOutputMaybef(Leftoverpi)=Leftover(mapOutputMaybefp)i-- | Apply a function to all the input values of a @Pipe@.---- Since 0.5.0mapInput::Monadm=>(i1->i2)-- ^ map initial input to new input->(l2->Maybel1)-- ^ map new leftovers to initial leftovers->Pipel2i2oumr->Pipel1i1oumrmapInputff'(HaveOutputpco)=HaveOutput(mapInputff'p)comapInputff'(NeedInputpc)=NeedInput(mapInputff'.p.f)(mapInputff'.c)mapInput__(Doner)=DonermapInputff'(PipeMmp)=PipeM(liftM(mapInputff')mp)mapInputff'(Leftoverpi)=maybeid(flipLeftover)(f'i)$mapInputff'p-- | Convert a list into a source.---- Since 0.3.0sourceList::Monadm=>[a]->Pipeliaum()sourceList=gowherego[]=Done()go(o:os)=HaveOutput(goos)(return())o{-# INLINE [1] sourceList #-}-- | The equivalent of @GHC.Exts.build@ for @Pipe@.---- Since 0.4.2build::Monadm=>(forallb.(o->b->b)->b->b)->Pipelioum()buildg=g(\op->HaveOutputp(return())o)(return()){-# RULES
"sourceList/build" forall (f :: (forall b. (a -> b -> b) -> b -> b)). sourceList (GHC.Exts.build f) = build f
#-}sourceToPipe::Monadm=>Sourcemo->Pipelioum()sourceToPipe(Done())=Done()sourceToPipe(PipeMmp)=PipeM(liftMsourceToPipemp)sourceToPipe(NeedInput_c)=sourceToPipe$c()sourceToPipe(HaveOutputpco)=HaveOutput(sourceToPipep)cosourceToPipe(Leftoverp())=sourceToPipepsinkToPipe::Monadm=>Sinkimr->PipelioumrsinkToPipe=go.injectLeftoverswherego(Doner)=Donergo(PipeMmp)=PipeM(liftMgomp)go(NeedInputpc)=NeedInput(go.p)(const$go$c())go(HaveOutput__o)=absurdogo(Leftover_l)=absurdlconduitToPipe::Monadm=>Conduitimo->Pipelioum()conduitToPipe=go.injectLeftoverswherego(Done())=Done()go(PipeMmp)=PipeM(liftMgomp)go(NeedInputpc)=NeedInput(go.p)(const$go$c())go(HaveOutputpco)=HaveOutput(gop)cogo(Leftover_l)=absurdl-- | Returns a tuple of the upstream and downstream results. Note that this-- will force consumption of the entire input stream.---- Since 0.5.0withUpstream::Monadm=>Pipelioumr->Pipelioum(u,r)withUpstreamdown=down>>=gowheregor=loopwhereloop=awaitE>>=either(\u->return(u,r))(\_->loop)-- | Unwraps a @ResumableSource@ into a @Source@ and a finalizer.---- A @ResumableSource@ represents a @Source@ which has already been run, and-- therefore has a finalizer registered. As a result, if we want to turn it-- into a regular @Source@, we need to ensure that the finalizer will be run-- appropriately. By appropriately, I mean:---- * If a new finalizer is registered, the old one should not be called.-- * If the old one is called, it should not be called again.---- This function returns both a @Source@ and a finalizer which ensures that the-- above two conditions hold. Once you call that finalizer, the @Source@ is-- invalidated and cannot be used.---- Since 0.5.2unwrapResumable::MonadIOm=>ResumableSourcemo->m(Sourcemo,m())unwrapResumable(ResumableSourcesrcfinal)=doref<-liftIO$I.newIORefTrueletfinal'=dox<-liftIO$I.readIORefrefwhenxfinalreturn(liftIO(I.writeIORefrefFalse)>>src,final')