{-# LANGUAGE UndecidableInstances #-} -- | Another complete example for the library module Main where import Data.Dynamic import Control.Eff import Control.Eff.Concurrent.SingleThreaded import Control.Eff.Concurrent.Protocol.StatefulServer import Control.Monad import Data.Foldable import Control.Lens import Control.Concurrent import Control.DeepSeq import qualified Data.Text as T import Data.Type.Pretty import Data.Default main :: IO () main = defaultMain (void counterExample) -- * First API data Counter deriving Typeable type instance ToPretty Counter = PutStr "counter" instance HasPdu Counter where data instance Pdu Counter x where Inc :: Pdu Counter 'Asynchronous Cnt :: Pdu Counter ('Synchronous Integer) deriving Typeable instance NFData (Pdu Counter x) where rnf Inc = () rnf Cnt = () counterExample :: Eff Effects () counterExample = do c <- spawnCounter let cp = _fromEndpoint c lift (threadDelay 500000) o <- logCounterObservations lift (threadDelay 500000) registerObserver @CounterChanged c o lift (threadDelay 500000) cast c Inc lift (threadDelay 500000) sendMessage cp ("test 123" :: String) cast c Inc lift (threadDelay 500000) cast c Inc sendMessage cp (12312312 :: Int) lift (threadDelay 500000) cast c Inc lift (threadDelay 500000) cast c Inc lift (threadDelay 500000) cast c Inc lift (threadDelay 500000) r <- call c Cnt lift (threadDelay 500000) lift (putStrLn ("r: " ++ show r)) lift (threadDelay 500000) lift (threadDelay 500000) data SupiDupi deriving Typeable type instance ToPretty SupiDupi = PutStr "supi dupi" instance HasPdu SupiDupi where data instance Pdu SupiDupi r where Whoopediedoo :: Bool -> Pdu SupiDupi ('Synchronous (Maybe ())) deriving Typeable instance Show (Pdu SupiDupi r) where show (Whoopediedoo True) = "woopediedooo" show (Whoopediedoo False) = "no woopy doopy" instance NFData (Pdu SupiDupi r) where rnf (Whoopediedoo b) = rnf b newtype CounterChanged = CounterChanged Integer deriving (Show, Typeable, NFData) type instance ToPretty CounterChanged = PutStr "counter changed" type SupiCounter = (Counter, ObserverRegistry CounterChanged, SupiDupi) type instance ToPretty (Counter, ObserverRegistry CounterChanged, SupiDupi) = PutStr "supi-counter" instance (IoLogging q) => Server SupiCounter (Processes q) where newtype instance Model SupiCounter = SupiCounterModel ( Integer , ObserverRegistry CounterChanged , Maybe (ReplyTarget SupiCounter (Maybe ())) ) data instance StartArgument SupiCounter = MkEmptySupiCounter setup _ _ = return (SupiCounterModel (0, emptyObserverRegistry, Nothing), ()) update _ _ = \case OnCall rt callReq -> case callReq of ToPdu1 Cnt -> sendReply rt =<< useModel supiCounter ToPdu2 _ -> error "unreachable" ToPdu3 (Whoopediedoo c) -> modifyModel @SupiCounter (supiTarget .~ if c then Just rt else Nothing) OnCast castReq -> case castReq of ToPdu1 Inc -> do val' <- view supiCounter <$> modifyAndGetModel (supiCounter %~ (+ 1)) zoomModel supiRegistry (observerRegistryNotify (CounterChanged val')) when (val' > 5) $ getAndModifyModel (supiTarget .~ Nothing) >>= traverse_ (\rt' -> sendReply rt' (Just ())) . view supiTarget ToPdu2 x -> zoomModel supiRegistry (observerRegistryHandlePdu x) ToPdu3 _ -> error "unreachable" OnDown pd -> do wasRemoved <- zoomModel supiRegistry (observerRegistryRemoveProcess @CounterChanged (downProcess pd)) if wasRemoved then logDebug ("removed: " <> T.pack (show pd)) else logError ("unexpected: " <> T.pack (show pd)) other -> logWarning (T.pack (show other)) supiCounter :: Lens' (Model SupiCounter) Integer supiCounter = lens (\(SupiCounterModel (x,_,_)) -> x) (\(SupiCounterModel (_,y,z)) x -> SupiCounterModel (x,y,z)) supiRegistry :: Lens' (Model SupiCounter) (ObserverRegistry CounterChanged) supiRegistry = lens (\(SupiCounterModel (_,y,_)) -> y) (\(SupiCounterModel (x,_,z)) y -> SupiCounterModel (x,y,z)) supiTarget :: Lens' (Model SupiCounter) (Maybe (ReplyTarget SupiCounter (Maybe ()))) supiTarget = lens (\(SupiCounterModel (_,_,z)) -> z) (\(SupiCounterModel (x,y,_)) z -> SupiCounterModel (x,y,z)) spawnCounter :: (IoLogging q) => Eff (Processes q) ( Endpoint SupiCounter ) spawnCounter = startLink MkEmptySupiCounter deriving instance Show (Pdu Counter x) logCounterObservations :: (IoLogging q, Typeable q) => Eff (Processes q) (Endpoint (Observer CounterChanged)) logCounterObservations = startLink OCCStart instance Member Logs q => Server (Observer CounterChanged) (Processes q) where data instance StartArgument (Observer CounterChanged) = OCCStart newtype instance Model (Observer CounterChanged) = CounterChangedModel () deriving Default update _ _ e = case e of OnCast (Observed msg) -> logInfo' ("observerRegistryNotify: " ++ show msg) _ -> logNotice (T.pack (show e))