{-# LANGUAGE DeriveAnyClass, DeriveFunctor, DerivingStrategies, FlexibleContexts, FlexibleInstances, GeneralizedNewtypeDeriving, KindSignatures, MultiParamTypeClasses, TypeOperators, UndecidableInstances #-}
module Control.Effect.Trace
( Trace(..)
, trace
, runTraceByPrinting
, TraceByPrintingC(..)
, runTraceByIgnoring
, TraceByIgnoringC(..)
, runTraceByReturning
, TraceByReturningC(..)
) where
import Control.Applicative (Alternative(..))
import Control.Effect.Carrier
import Control.Effect.State
import Control.Effect.Sum
import Control.Monad (MonadPlus(..))
import Control.Monad.Fail
import Control.Monad.IO.Class
import Control.Monad.Trans.Class
import Data.Bifunctor (first)
import System.IO
data Trace (m :: * -> *) k = Trace
{ traceMessage :: String
, traceCont :: k
} deriving stock Functor
deriving anyclass (HFunctor, Effect)
trace :: (Member Trace sig, Carrier sig m) => String -> m ()
trace message = send (Trace message (pure ()))
runTraceByPrinting :: TraceByPrintingC m a -> m a
runTraceByPrinting = runTraceByPrintingC
newtype TraceByPrintingC m a = TraceByPrintingC { runTraceByPrintingC :: m a }
deriving newtype (Alternative, Applicative, Functor, Monad, MonadFail, 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 #-}
runTraceByIgnoring :: TraceByIgnoringC m a -> m a
runTraceByIgnoring = runTraceByIgnoringC
newtype TraceByIgnoringC m a = TraceByIgnoringC { runTraceByIgnoringC :: m a }
deriving newtype (Alternative, Applicative, Functor, Monad, MonadFail, 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 #-}
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, 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)))