module Theatre
(
Actor,
graceful,
disgraceful,
suicidal,
tell,
kill,
)
where
import Theatre.Prelude
import qualified Control.Concurrent.Chan.Unagi as E
import qualified SlaveThread as F
data Actor message =
Actor {
tell :: message -> IO (),
kill :: IO ()
}
instance Semigroup (Actor message) where
(<>) (Actor leftTell leftKill) (Actor rightTell rightKill) =
Actor tell kill
where
tell message =
leftTell message >> rightTell message
kill =
leftKill >> rightKill
instance Monoid (Actor message) where
mempty =
Actor (const (return ())) (return ())
mappend =
(<>)
instance Contravariant Actor where
contramap fn (Actor tell kill) =
Actor (tell . fn) kill
instance Divisible Actor where
conquer =
mempty
divide divisor (Actor leftTell leftKill) (Actor rightTell rightKill) =
Actor tell kill
where
tell message =
case divisor message of
(leftMessage, rightMessage) -> leftTell leftMessage >> rightTell rightMessage
kill =
leftKill >> rightKill
instance Decidable Actor where
lose fn =
Actor (const (return ()) . absurd . fn) (return ())
choose choice (Actor leftTell leftKill) (Actor rightTell rightKill) =
Actor tell kill
where
tell =
either leftTell rightTell . choice
kill =
leftKill >> rightKill
graceful :: (message -> IO ()) -> IO (Actor message)
graceful interpretMessage =
do
(inChan, outChan) <- E.newChan
F.fork $ fix $ \loop -> {-# SCC "graceful/loop" #-} do
message <- E.readChan outChan
case message of
Just payload ->
do
interpretMessage payload
loop
Nothing ->
return ()
return (Actor (E.writeChan inChan . Just) (E.writeChan inChan Nothing))
disgraceful :: (message -> IO ()) -> IO (Actor message)
disgraceful receiver =
suicidal (\producer -> forever (producer >>= receiver))
suicidal :: (IO message -> IO ()) -> IO (Actor message)
suicidal receiver =
do
(inChan, outChan) <- E.newChan
threadId <- F.fork (receiver (E.readChan outChan))
return (Actor (E.writeChan inChan) (killThread threadId))