    {-# 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  m a
    p = m a -> m (future a)
    forall a. m a -> m (future a)
    forall (future :: * -> *) (m :: * -> *) a.
    ParFuture future m =>
    m a -> m (future a)
    spawn_ (do a
    x <- m a
    p; a -> m a -> m a
    forall a b. NFData a => a -> b -> b
    deepseq a
    x (a -> m a
    forall a. a -> m a
    forall (m :: * -> *) a. Monad m => a -> m a
    return a
    x))
      spawnP a
    a = m a -> m (future a)
    forall a. NFData a => m a -> m (future a)
    forall (future :: * -> *) (m :: * -> *) a.
    (ParFuture future m, NFData a) =>
    m a -> m (future a)
    spawn (a -> m a
    forall a. a -> m a
    forall (m :: * -> *) a. Monad m => a -> m a
    return a
    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 ivar a
    v a
    a = a -> m () -> m ()
    forall a b. NFData a => a -> b -> b
    deepseq a
    a (ivar a -> a -> m ()
    forall a. ivar a -> a -> m ()
    forall (ivar :: * -> *) (m :: * -> *) a.
    ParIVar ivar m =>
    ivar a -> a -> m ()
    put_ ivar a
    v a
    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
    a = a -> m (ivar a) -> m (ivar a)
    forall a b. NFData a => a -> b -> b
    deepseq a
    a (a -> m (ivar a)
    forall a. a -> m (ivar a)
    forall (ivar :: * -> *) (m :: * -> *) a.
    ParIVar ivar m =>
    a -> m (ivar a)
    newFull_ a
    a)

      -- | creates a new @IVar@ that contains a value (head-strict only)
      newFull_ ::  a -> m (ivar a)
      newFull_ a
    a = do ivar a
    v <- m (ivar a)
    forall a. m (ivar a)
    forall (ivar :: * -> *) (m :: * -> *) a.
    ParIVar ivar m =>
    m (ivar a)
    new
                      -- This is usually inefficient!
    		  ivar a -> a -> m ()
    forall a. ivar a -> a -> m ()
    forall (ivar :: * -> *) (m :: * -> *) a.
    ParIVar ivar m =>
    ivar a -> a -> m ()
    put_ ivar a
    v a
    a
    		  ivar a -> m (ivar a)
    forall a. a -> m a
    forall (m :: * -> *) a. Monad m => a -> m a
    return ivar a
    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 :: forall (v :: * -> *) (m :: * -> *). ParFuture v m => m Int
    t1 = do
      v Int
    x <- m Int -> m (v Int)
    forall a. NFData a => m a -> m (v a)
    forall (future :: * -> *) (m :: * -> *) a.
    (ParFuture future m, NFData a) =>
    m a -> m (future a)
    spawn (Int -> m Int
    forall a. a -> m a
    forall (m :: * -> *) a. Monad m => a -> m a
    return Int
    3)
      v Int -> m Int
    forall a. v a -> m a
    forall (future :: * -> *) (m :: * -> *) a.
    ParFuture future m =>
    future a -> m a
    get v Int
    x

    t2 :: (ParIVar v m) => m Int
    t2 :: forall (v :: * -> *) (m :: * -> *). ParIVar v m => m Int
    t2 = do
      v String
    x <- m (v String)
    forall a. m (v a)
    forall (ivar :: * -> *) (m :: * -> *) a.
    ParIVar ivar m =>
    m (ivar a)
    new
      v String -> String -> m ()
    forall a. NFData a => v a -> a -> m ()
    forall (ivar :: * -> *) (m :: * -> *) a.
    (ParIVar ivar m, NFData a) =>
    ivar a -> a -> m ()
    put v String
    x String
    "hi"
      Int -> m Int
    forall a. a -> m a
    forall (m :: * -> *) a. Monad m => a -> m a
    return Int
    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]