{-| Minimalistic actor library. -} module Theatre ( Actor, -- * Construction graceful, disgraceful, suicidal, -- * Usage tell, kill, ) where import Theatre.Prelude import qualified Control.Concurrent.Chan.Unagi as E import qualified SlaveThread as F {-| Actor, which processes the messages of type @message@. An abstraction over the message channel, thread-forking and killing. -} data Actor message = Actor { {-| Send a message to the actor -} tell :: message -> IO (), {-| Kill the actor -} 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 {-| An actor which cannot die by itself unless explicitly killed. Given an interpreter of messages, forks a thread to run the computation on and produces a handle to address that actor. Killing that actor will make it process all the messages in the queue first. All the messages sent to it after killing won't be processed. -} graceful :: (message -> IO ()) {-^ Interpreter of a message -} -> 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)) {-| An actor which cannot die by itself unless explicitly killed. Given an interpreter of messages, forks a thread to run the computation on and produces a handle to address that actor. -} disgraceful :: (message -> IO ()) {-^ Interpreter of a message -} -> IO (Actor message) disgraceful receiver = suicidal (\producer -> forever (producer >>= receiver)) {-| An actor, whose interpreter can decide that the actor should die. Given an implementation of a receiver loop of messages, forks a thread to run that receiver on and produces a handle to address that actor. -} suicidal :: (IO message -> IO ()) {-^ A message receiver loop. When the loop exits, the actor dies -} -> IO (Actor message) suicidal receiver = do (inChan, outChan) <- E.newChan threadId <- F.fork (receiver (E.readChan outChan)) return (Actor (E.writeChan inChan) (killThread threadId))