content: {-# LANGUAGE Trustworthy #-}
  -- |
  -- Copyright: (c) 2021 Xy Ren
  -- License: BSD3
  -- Maintainer: xy.r@outlook.com
  -- Stability: experimental
  -- Portability: non-portable (GHC only)
  module Cleff.Output
    ( -- * Effect
      Output (..)
      -- * Operations
    , output
      -- * Interpretations
    , outputToListState
    , outputToWriter
    , ignoreOutput
    , runOutputEff
    , mapOutput
    , bindOutput
    ) where
  
  import           Cleff
  import           Cleff.State
  import           Cleff.Writer
  
  -- * Effect
  
  -- | An effect that is capable of producing outputs, for example writing to a log file or an output stream.
  data Output o :: Effect where
    Output :: o -> Output o m ()
  
  -- * Operations
  
  makeEffect_ ''Output
  
  -- | Produce an output value.
  output :: Output o :> es => o -> Eff es ()
  
  -- * Interpretations
  
  -- | Run an 'Output' effect by accumulating a list. Note that outputs are being prepended to the head of the list, so
  -- in many cases you would want to 'reverse' the result.
  outputToListState :: Eff (Output o : es) ~> Eff (State [o] : es)
  outputToListState = reinterpret \case
    Output x -> modify (x :)
  
  -- | Run an 'Output' effect by translating it into a 'Writer'.
  outputToWriter :: (o -> o') -> Eff (Output o : es) ~> Eff (Writer o' : es)
  outputToWriter f = reinterpret \case
    Output x -> tell $ f x
  
  -- | Ignore outputs of an 'Output' effect altogether.
  ignoreOutput :: Eff (Output o : es) ~> Eff es
  ignoreOutput = interpret \case
    Output _ -> pure ()
  
  -- | Run an 'Output' effect by performing a computation for each output.
  runOutputEff :: (o -> Eff es ()) -> Eff (Output o : es) ~> Eff es
  runOutputEff m = interpret \case
    Output x -> m x
  
  -- | Transform an 'Output' effect into another one already in the effect stack, by a pure function.
  --
  -- @since 0.2.1.0
  mapOutput :: Output o' :> es => (o -> o') -> Eff (Output o : es) ~> Eff es
  mapOutput f = interpret \case
    Output x -> output $ f x
  
  -- | Transform an 'Output' effect into another one already in the effect stack, by an effectful computation.
  --
  -- @since 0.2.1.0
  bindOutput :: Output o' :> es => (o -> Eff es o') -> Eff (Output o : es) ~> Eff es
  bindOutput f = interpret \case
    Output x -> output =<< f x
  