1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
|
{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, CPP,
FlexibleInstances, UndecidableInstances #-}
-- UndecidableInstances
{-|
This module establishes a class hierarchy that captures the
interfaces of @Par@ monads. There are two layers: simple futures
('ParFuture') and full @IVars@ ('ParIVar'). All @Par@ monads are
expected to implement the former, some also implement the latter.
For more documentation of the programming model, see
* The "Control.Monad.Par" module in the @monad-par@ package.
* The wiki\/tutorial (<http://www.haskell.org/haskellwiki/Par_Monad:_A_Parallelism_Tutorial>)
* The original paper (<http://www.cs.indiana.edu/~rrnewton/papers/haskell2011_monad-par.pdf>)
* Tutorial slides (<http://community.haskell.org/~simonmar/slides/CUFP.pdf>)
* Other slides (<http://www.cs.ox.ac.uk/ralf.hinze/WG2.8/28/slides/simon.pdf>, <http://www.cs.indiana.edu/~rrnewton/talks/2011_HaskellSymposium_ParMonad.pdf>)
-}
--
module Control.Monad.Par.Class
(
-- * Futures
ParFuture(..)
-- * IVars
, ParIVar(..)
-- RRN: Not releasing this interface until there is a nice implementation of it:
-- Channels (Streams)
-- , ParChan(..)
, NFData() -- This is reexported.
)
where
import Control.DeepSeq
--------------------------------------------------------------------------------
-- | @ParFuture@ captures the class of Par monads which support
-- futures. This level of functionality subsumes @par@/@pseq@ and is
-- similar to the "Control.Parallel.Strategies.Eval" monad.
--
-- A minimal implementation consists of `spawn_` and `get`.
-- However, for monads that are also a member of `ParIVar` it is
-- typical to simply define `spawn` in terms of `fork`, `new`, and `put`.
class Monad m => ParFuture future m | m -> future where
-- | Create a potentially-parallel computation, and return a /future/
-- (or /promise/) that can be used to query the result of the forked
-- computataion.
--
-- > spawn p = do
-- > r <- new
-- > fork (p >>= put r)
-- > return r
--
spawn :: NFData a => m a -> m (future a)
-- | Like 'spawn', but the result is only head-strict, not fully-strict.
spawn_ :: m a -> m (future a)
-- | Wait for the result of a future, and then return it.
get :: future a -> m a
-- | Spawn a pure (rather than monadic) computation. Fully-strict.
--
-- > spawnP = spawn . return
spawnP :: NFData a => a -> m (future a)
-- Default implementations:
spawn p = spawn_ (do x <- p; deepseq x (return x))
spawnP a = spawn (return a)
--------------------------------------------------------------------------------
-- | @ParIVar@ builds on futures by adding full /anyone-writes, anyone-reads/ IVars.
-- These are more expressive but may not be supported by all distributed schedulers.
--
-- A minimal implementation consists of `fork`, `put_`, and `new`.
class ParFuture ivar m => ParIVar ivar m | m -> ivar where
-- | Forks a computation to happen in parallel. The forked
-- computation may exchange values with other computations using
-- @IVar@s.
fork :: m () -> m ()
-- | creates a new @IVar@
new :: m (ivar a)
-- | put a value into a @IVar@. Multiple 'put's to the same @IVar@
-- are not allowed, and result in a runtime error.
--
-- 'put' fully evaluates its argument, which therefore must be an
-- instance of 'NFData'. The idea is that this forces the work to
-- happen when we expect it, rather than being passed to the consumer
-- of the @IVar@ and performed later, which often results in less
-- parallelism than expected.
--
-- Sometimes partial strictness is more appropriate: see 'put_'.
--
put :: NFData a => ivar a -> a -> m ()
put v a = deepseq a (put_ v a)
-- | like 'put', but only head-strict rather than fully-strict.
put_ :: ivar a -> a -> m ()
-- Extra API routines that have default implementations:
-- | creates a new @IVar@ that contains a value
newFull :: NFData a => a -> m (ivar a)
newFull a = deepseq a (newFull_ a)
-- | creates a new @IVar@ that contains a value (head-strict only)
newFull_ :: a -> m (ivar a)
newFull_ a = do v <- new
-- This is usually inefficient!
put_ v a
return v
--------------------------------------------------------------------------------
-- class ParYieldable ??
-- TODO: I think we should add yield officially:
-- Allows other parallel computations to progress. (should not be
-- necessary in most cases).
-- yield :: m ()
--------------------------------------------------------------------------------
-- | @ParChan@ provides communication via streams of values between
-- computations in a Par monad. Channels in this case are split
-- into separate send and receive ports.
--
-- The critical thing to know about @Chan@s in @Par@ monads is that
-- while the @recv@ method destructively advances the position of
-- the consumer's \"cursor\" in the stream, this is only observable
-- in the /local/ @Par@ thread. That is, at @fork@ points it is
-- necessary to give the child computation a separate set of stream
-- cursors so that it observes the same sequences as the parent.
class Monad m => ParChan snd rcv m | m -> snd, m -> rcv where
-- | Create a new communication channel, with separate send and receive ports.
newChan :: m (snd a, rcv a)
-- | Receive a message on a channel in a synchronous, blocking manner.
recv :: rcv a -> m a
-- | Send a message on a channel. This may or may not block.
send :: snd a -> a -> m ()
----------------------------------------------------------------------------------------------------
-- t1 :: P.Par Int
-- If the ParIVar => ParFuture instance exists the following is sufficient:
t1 :: (ParFuture v m) => m Int
t1 = do
x <- spawn (return 3)
get x
t2 :: (ParIVar v m) => m Int
t2 = do
x <- new
put x "hi"
return 3
-- TODO: SPECIALIZE generic routines for the default par monad (and possibly ParRNG)?
-- SPECIALISE parMap :: (NFData b) => (a -> b) -> [a] -> Par [b]
-- SPECIALISE parMapM :: (NFData b) => (a -> Par b) -> [a] -> Par [b]
|