packages feed

atelier-core-0.3.0.0: src/Atelier/Effects/Publishing/Pub.hs

module Atelier.Effects.Publishing.Pub
    ( -- * Effect
      Pub (..)
    , publish

      -- * Interpeters
    , runNoOp
    , toWriter
    , map
    , mapM
    , consume
    ) where

import Effectful (Effect)
import Effectful.Dispatch.Dynamic (interpret_)
import Effectful.TH (makeEffect)
import Effectful.Writer.Static.Shared (Writer, tell)
import Prelude hiding (map, mapM)


-- | Effect for publishing events of type @event@.
data Pub (event :: Type) :: Effect where
    -- | Publish an event to all current subscribers.
    Publish :: event -> Pub event m ()


makeEffect ''Pub


runNoOp :: forall event es a. Eff (Pub event : es) a -> Eff es a
runNoOp = interpret_ \(Publish _) -> pure ()


-- | Handler that uses a provided Writer effect instead of actually publishing.
-- Useful for testing and inspecting what events were published.
toWriter :: forall event es a. (Writer [event] :> es) => Eff (Pub event : es) a -> Eff es a
toWriter =
    interpret_ \case
        Publish event -> tell [event]


-- | Convert published events of one type into another, utilizing an existing
-- effect in the effect stack.
map :: forall e1 e2 es a. (Pub e2 :> es) => (e1 -> e2) -> Eff (Pub e1 : es) a -> Eff es a
map f = interpret_ \(Publish event) -> publish $ f event


-- | Convert published events of one type into another with an effectful
-- transformation, utilizing an existing effect int he effect stack.
mapM :: forall e1 e2 es a. (Pub e2 :> es) => (e1 -> Eff es e2) -> Eff (Pub e1 : es) a -> Eff es a
mapM f = interpret_ \(Publish event) -> f event >>= publish


-- | Perform an effectful action for each published event.
consume :: (event -> Eff es ()) -> Eff (Pub event : es) a -> Eff es a
consume handler = interpret_ \case
    Publish event -> handler event