-- Copyright 2013 Kevin Backhouse.

-- | 'Control.Monad.MultiPass.ThreadContext.MonoidTC' defines a thread
-- context which is used to gather values from all the threads of the
-- program. The values to be gathered must be instances of the
-- 'Data.Monoid' class.

module Control.Monad.MultiPass.ThreadContext.MonoidTC ( MonoidTC(..) )
where

import Control.Monad.Writer.Strict
import Control.Monad.MultiPass

-- | MonoidTC is a thread context which uses the Monoid interface to
-- combine the values from multiple threads. Instances of the Monoid
-- class are expected to be associative, so the value computed by
-- MonoidTC is invariant under changes to the number of threads that
-- are spawned.
newtype MonoidTC a = MonoidTC { unwrapMonoidTC :: a }

instance Monoid a => Monoid (MonoidTC a) where
  mempty = MonoidTC mempty

  mappend (MonoidTC x) (MonoidTC y) =
    MonoidTC (mappend x y)

instance Monoid a => ThreadContext r w (MonoidTC a) where
  splitThreadContext _ _ _ =
    return $ mempty

  mergeThreadContext n f x =
    execWriterT $
    do tell x
       sequence_
         [ do y <- lift $ f i
              tell y
         | i <- [0 .. n-1]
         ]

instance Monoid a => NextThreadContext r w tc gc (MonoidTC a) where
  nextThreadContext _ _ _ _ =
    return mempty