{-
Copyright 2010 Mario Blazevic
This file is part of the Streaming Component Combinators (SCC) project.
The SCC project is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later
version.
SCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with SCC. If not, see
<http://www.gnu.org/licenses/>.
-}-- | This module defines classes of monads that can perform multiple computations in parallel and, more importantly,-- combine the results of those parallel computations.-- -- There are two classes exported by this module, 'MonadParallel' and 'MonadFork'. The former is more generic, but the-- latter is easier to use: when invoking any expensive computation that could be performed in parallel, simply wrap the-- call in 'forkExec'. The function immediately returns a handle to the running computation. The handle can be used to-- obtain the result of the computation when needed:---- @-- do child <- forkExec expensive-- otherStuff-- result <- child-- @---- In this example, the computations /expensive/ and /otherStuff/ would be performed in parallel. When using the-- 'MonadParallel' class, both parallel computations must be specified at once:---- @-- bindM2 (\\ childResult otherResult -> ...) expensive otherStuff-- @---- In either case, for best results the costs of the two computations should be roughly equal.---- Any monad that is an instance of the 'MonadFork' class is also an instance of the 'MonadParallel' class, and the-- following law should hold:-- -- @ bindM2 f ma mb = do {a' <- forkExec ma; b <- mb; a <- a'; f a b} @ ---- When operating with monads free of side-effects, such as 'Identity' or 'Maybe', 'forkExec' is equivalent to 'return'-- and 'bindM2' is equivalent to @ \\ f ma mb -> do {a <- ma; b <- mb; f a b} @ &#x2014; the only difference is in the-- resource utilisation. With the 'IO' monad, on the other hand, there may be visible difference in the results because-- the side effects of /ma/ and /mb/ may be arbitrarily reordered.{-# LANGUAGE ScopedTypeVariables #-}moduleControl.Monad.Parallel(-- * ClassesMonadParallel(..),MonadFork(..),bindM3,-- * Control.Monad equivalentsliftM2,liftM3,ap,sequence,sequence_,mapM,replicateM,replicateM_)whereimportPrelude()importControl.Concurrent(forkIO)importControl.Concurrent.MVar(newEmptyMVar,putMVar,takeMVar,readMVar)importControl.Exception(SomeException,throwIO,mask,try)importControl.Monad(Monad,(>>=),return,liftM)importControl.Monad.Trans.Identity(IdentityT(IdentityT,runIdentityT))importControl.Monad.Trans.Maybe(MaybeT(MaybeT,runMaybeT))importControl.Monad.Trans.Error(ErrorT(ErrorT,runErrorT),Error)importControl.Monad.Trans.List(ListT(ListT,runListT))importControl.Monad.Trans.Reader(ReaderT(ReaderT,runReaderT))importControl.Parallel(par,pseq)importData.Either(Either(..),either)importData.Function(($),(.),const)importData.Functor.Identity(Identity)importData.Int(Int)importData.List((++),foldr,map,replicate)importData.Maybe(Maybe(Just,Nothing))importSystem.IO(IO)-- | Class of monads that can perform two computations in parallel and bind their results together.classMonadm=>MonadParallelmwhere-- | Perform two monadic computations in parallel; when they are both finished, pass the results to the function.-- Apart from the possible ordering of side effects, this function is equivalent to-- @\\f ma mb-> do {a <- ma; b <- mb; f a b}@bindM2::(a->b->mc)->ma->mb->mcbindM2fmamb=letma'=ma>>=returnmb'=mb>>=returninma'`par`(mb'`pseq`do{a<-ma';b<-mb';fab})-- | Class of monads that can fork a parallel computation.classMonadParallelm=>MonadForkmwhere-- | Fork a child monadic computation to be performed in parallel with the current one.forkExec::ma->m(ma)forkExece=letresult=e>>=returninresult`par`(returnresult)-- | Perform three monadic computations in parallel; when they are all finished, pass their results to the function.bindM3::MonadParallelm=>(a->b->c->md)->ma->mb->mc->mdbindM3fmambmc=bindM2(\f'c->f'c)(liftM2fmamb)mc-- | Like 'Control.Monad.liftM2', but evaluating its two monadic arguments in parallel.liftM2::MonadParallelm=>(a->b->c)->ma->mb->mcliftM2fm1m2=bindM2(\ab->return(fab))m1m2-- | Like 'Control.Monad.liftM3', but evaluating its three monadic arguments in parallel.liftM3::(MonadParallelm)=>(a1->a2->a3->r)->ma1->ma2->ma3->mrliftM3fm1m2m3=bindM3(\abc->return(fabc))m1m2m3-- | Like 'Control.Monad.ap', but evaluating the function and its argument in parallel.ap::MonadParallelm=>m(a->b)->ma->mbapmfma=bindM2(\fa->return(fa))mfma-- | Like 'Control.Monad.sequence', but executing the actions in parallel.sequence::MonadParallelm=>[ma]->m[a]sequencems=foldrk(return[])mswherekmm'=liftM2(:)mm'-- | Like 'Control.Monad.sequence_', but executing the actions in parallel.sequence_::MonadParallelm=>[ma]->m()sequence_ms=foldr(liftM2(\__->()))(return())ms-- | Like 'Control.Monad.mapM', but applying the function to the individual list items in parallel.mapM::MonadParallelm=>(a->mb)->[a]->m[b]mapMflist=sequence(mapflist)-- | Like 'Control.Monad.replicateM', but executing the action multiple times in parallel.replicateM::MonadParallelm=>Int->ma->m[a]replicateMnaction=sequence(replicatenaction)-- | Like 'Control.Monad.replicateM_', but executing the action multiple times in parallel.replicateM_::MonadParallelm=>Int->ma->m()replicateM_naction=sequence_(replicatenaction)-- | Any monad that allows the result value to be extracted, such as `Identity` or `Maybe` monad, can implement-- `bindM2` by using `par`.instanceMonadParallelIdentityinstanceMonadParallelMaybeinstanceMonadParallel[]instanceMonadParallel((->)r)wherebindM2fmambr=leta=marb=mbrina`par`(b`pseq`fabr)-- | IO is parallelizable by `forkIO`.instanceMonadParallelIOwherebindM2fmamb=dowaitForB<-forkExecmba<-mab<-waitForBfabinstanceMonadParallelm=>MonadParallel(IdentityTm)wherebindM2fmamb=IdentityT(bindM2f'(runIdentityTma)(runIdentityTmb))wheref'ab=runIdentityT(fab)instanceMonadParallelm=>MonadParallel(MaybeTm)wherebindM2fmamb=MaybeT(bindM2f'(runMaybeTma)(runMaybeTmb))wheref'(Justa)(Justb)=runMaybeT(fab)f'__=returnNothinginstance(MonadParallelm,Errore)=>MonadParallel(ErrorTem)wherebindM2fmamb=ErrorT(bindM2f'(runErrorTma)(runErrorTmb))wheref'(Righta)(Rightb)=runErrorT(fab)f'(Lefte)_=return(Lefte)f'_(Lefte)=return(Lefte)instanceMonadParallelm=>MonadParallel(ListTm)wherebindM2fmamb=ListT(bindM2f'(runListTma)(runListTmb))wheref'asbs=foldrconcat(return[])[runListT(fab)|a<-as,b<-bs]concatmm'=do{x<-m;y<-m';return(x++y)}instanceMonadParallelm=>MonadParallel(ReaderTrm)wherebindM2fmamb=ReaderT(\r->bindM2(f'r)(runReaderTmar)(runReaderTmbr))wheref'rab=runReaderT(fab)rinstanceMonadForkMaybeinstanceMonadFork[]instanceMonadFork((->)r)whereforkExece=\r->letresult=erinresult`par`(returnresult)-- | IO is forkable by `forkIO`.instanceMonadForkIOwhereforkExecma=dov<-newEmptyMVar_<-mask$\restore->forkIO$try(restorema)>>=putMVarvreturn$readMVarv>>=either(\e->throwIO(e::SomeException))returninstanceMonadForkm=>MonadFork(IdentityTm)whereforkExecma=IdentityT(liftMIdentityT$forkExec(runIdentityTma))instanceMonadForkm=>MonadFork(MaybeTm)whereforkExecma=MaybeT(liftM(Just.MaybeT)$forkExec(runMaybeTma))instance(MonadForkm,Errore)=>MonadFork(ErrorTem)whereforkExecma=ErrorT(liftM(Right.ErrorT)$forkExec(runErrorTma))instanceMonadForkm=>MonadFork(ListTm)whereforkExecma=ListT(liftM((:[]).ListT)$forkExec(runListTma))instanceMonadForkm=>MonadFork(ReaderTrm)whereforkExecma=ReaderT(\r->liftM(ReaderT.const)$forkExec(runReaderTmar))