fused-effects-0.5.0.1: src/Control/Effect/Trace.hs
{-# LANGUAGE DeriveAnyClass, DeriveFunctor, DeriveGeneric, DerivingStrategies, FlexibleContexts, FlexibleInstances, GeneralizedNewtypeDeriving, MultiParamTypeClasses, TypeOperators, UndecidableInstances #-}
module Control.Effect.Trace
( -- * Trace effect
Trace(..)
, trace
-- * Trace carriers
, runTraceByPrinting
, TraceByPrintingC(..)
, runTraceByIgnoring
, TraceByIgnoringC(..)
, runTraceByReturning
, TraceByReturningC(..)
-- * Re-exports
, Carrier
, Member
, run
) where
import Control.Applicative (Alternative(..))
import Control.Effect.Carrier
import Control.Effect.State
import Control.Monad (MonadPlus(..))
import qualified Control.Monad.Fail as Fail
import Control.Monad.Fix
import Control.Monad.IO.Class
import Control.Monad.Trans.Class
import Data.Bifunctor (first)
import GHC.Generics (Generic1)
import System.IO
data Trace m k = Trace
{ traceMessage :: String
, traceCont :: m k
}
deriving stock (Functor, Generic1)
deriving anyclass (HFunctor, Effect)
-- | Append a message to the trace log.
trace :: (Member Trace sig, Carrier sig m) => String -> m ()
trace message = send (Trace message (pure ()))
-- | Run a 'Trace' effect, printing traces to 'stderr'.
runTraceByPrinting :: TraceByPrintingC m a -> m a
runTraceByPrinting = runTraceByPrintingC
newtype TraceByPrintingC m a = TraceByPrintingC { runTraceByPrintingC :: m a }
deriving newtype (Alternative, Applicative, Functor, Monad, Fail.MonadFail, MonadFix, MonadIO, MonadPlus)
instance MonadTrans TraceByPrintingC where
lift = TraceByPrintingC
{-# INLINE lift #-}
instance (MonadIO m, Carrier sig m) => Carrier (Trace :+: sig) (TraceByPrintingC m) where
eff (L (Trace s k)) = liftIO (hPutStrLn stderr s) *> k
eff (R other) = TraceByPrintingC (eff (handleCoercible other))
{-# INLINE eff #-}
-- | Run a 'Trace' effect, ignoring all traces.
--
-- prop> run (runTraceByIgnoring (trace a *> pure b)) === b
runTraceByIgnoring :: TraceByIgnoringC m a -> m a
runTraceByIgnoring = runTraceByIgnoringC
newtype TraceByIgnoringC m a = TraceByIgnoringC { runTraceByIgnoringC :: m a }
deriving newtype (Alternative, Applicative, Functor, Monad, Fail.MonadFail, MonadFix, MonadIO, MonadPlus)
instance MonadTrans TraceByIgnoringC where
lift = TraceByIgnoringC
{-# INLINE lift #-}
instance Carrier sig m => Carrier (Trace :+: sig) (TraceByIgnoringC m) where
eff (L trace) = traceCont trace
eff (R other) = TraceByIgnoringC (eff (handleCoercible other))
{-# INLINE eff #-}
-- | Run a 'Trace' effect, returning all traces as a list.
--
-- prop> run (runTraceByReturning (trace a *> trace b *> pure c)) === ([a, b], c)
runTraceByReturning :: Functor m => TraceByReturningC m a -> m ([String], a)
runTraceByReturning = fmap (first reverse) . runState [] . runTraceByReturningC
newtype TraceByReturningC m a = TraceByReturningC { runTraceByReturningC :: StateC [String] m a }
deriving newtype (Alternative, Applicative, Functor, Monad, Fail.MonadFail, MonadFix, MonadIO, MonadPlus, MonadTrans)
instance (Carrier sig m, Effect sig) => Carrier (Trace :+: sig) (TraceByReturningC m) where
eff (L (Trace m k)) = TraceByReturningC (modify (m :)) *> k
eff (R other) = TraceByReturningC (eff (R (handleCoercible other)))
-- $setup
-- >>> :seti -XFlexibleContexts
-- >>> import Test.QuickCheck
-- >>> import Control.Effect.Pure