packages feed

extensible-effects-concurrent-0.24.0: examples/example-2/Main.hs

-- | Another complete example for the library
module Main where

import           Data.Dynamic
import           Control.Eff
import           Control.Eff.Concurrent
import           Control.Eff.Concurrent.Protocol.Server
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

main :: IO ()
main = defaultMain (void counterExample)

-- * First API

data Counter deriving Typeable

type instance ToPretty Counter = PutStr "counter"

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
  :: (Typeable q, LogsTo IO q, Lifted IO q)
  => Eff (InterruptableProcess q) ()
counterExample = do
  c <- spawnCounter
  let cp = _fromEndpoint c
  lift (threadDelay 500000)
  o <- logCounterObservations
  lift (threadDelay 500000)
  registerObserver o c
  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"

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 (LogIo q) => Server SupiCounter (InterruptableProcess q) where

  type instance Model SupiCounter = (Integer, Observers CounterChanged, Maybe (RequestOrigin SupiCounter (Maybe ())))

  data instance StartArgument SupiCounter (InterruptableProcess q) = MkEmptySupiCounter

  setup _ = return ((0, emptyObservers, Nothing), ())

  update _ = \case
    OnRequest req ->
      case req of
        Call orig callReq ->
          case callReq of
            ToPdu1 Cnt ->
              sendReply orig =<< useModel @SupiCounter _1
            ToPdu2 _ -> error "unreachable"
            ToPdu3 (Whoopediedoo c) ->
              modifyModel @SupiCounter (_3 .~ if c then Just orig else Nothing)

        Cast castReq ->
          case castReq of
            ToPdu1 Inc -> do
              val' <- view _1 <$> modifyAndGetModel @SupiCounter (_1 %~ (+ 1))
              zoomModel @SupiCounter _2 (observed (CounterChanged val'))
              when (val' > 5) $
                getAndModifyModel @SupiCounter (_3 .~ Nothing)
                >>= traverse_ (flip sendReply (Just ())) . view _3
            ToPdu2 x ->
              zoomModel @SupiCounter _2 (handleObserverRegistration x)
            ToPdu3 _ -> error "unreachable"

    other -> logWarning (T.pack (show other))

spawnCounter :: (LogsTo IO q, Lifted IO q) => Eff (InterruptableProcess q) ( Endpoint SupiCounter )
spawnCounter = start MkEmptySupiCounter


deriving instance Show (Pdu Counter x)

logCounterObservations
  :: (LogsTo IO q, Lifted IO q, Typeable q)
  => Eff (InterruptableProcess q) (Observer CounterChanged)
logCounterObservations = do
  svr <- start
          $ genServer @(Observer CounterChanged)
            (\_me -> pure (emptyObservers, ()))
            (\_me ->
                \case
                  OnRequest (Cast r) ->
                    handleObservations (\msg -> logInfo' ("observed: " ++ show msg)) r
                  wtf -> logNotice (T.pack (show wtf))
            )
            "counter logger"

  pure (toObserver svr)

type instance GenServerModel (Observer CounterChanged) = Observers CounterChanged
type instance GenServerSettings (Observer CounterChanged) = ()
type instance GenServerProtocol (Observer CounterChanged) = Observer CounterChanged