{-# 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 Control.Monad.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, 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, 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, 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