supervisor-0.1.1.0: Control/Monad/Supervisor/Trace.hs
{-# LANGUAGE ScopedTypeVariables, TypeSynonymInstances,
MultiParamTypeClasses, FlexibleInstances
,FlexibleContexts, UndecidableInstances #-}
-- | This module add a 'MonadLoc' instance to the 'Supervisor' monad. This instance generates a trace when a
-- uncaugh exception is raised.
--
-- . See the MonadLoc package: <http://hackage.haskell.org/package/monadloc>
--
-- The package control-monad-exception produces call stacks using @monadloc@, but the @Supervisor@ monad
-- produces execution traces thanks to the backtracking mechanism.
--
-- The trace is produced after the exception is raised. So it does not generate
-- overhead in normal execution.
--
-- For more finer control of exceptions, ej. for retrowing exceptions managed outside the Supervisor monad
-- , create your own instance
--
-- Execute the example at @Demos/TraceExample.hs@
--
--
-- > {-# OPTIONS -F -pgmF MonadLoc #-}
-- > module Demos.TraceExample (
-- >
-- > ) where
-- >
-- > import Control.Monad.Loc
-- > import Control.Monad.Supervisor.Trace
-- > import Control.Monad.Trans
-- >
-- > main= runTrace $ do
-- > liftIO $ print "hello"
-- >
-- > example
-- >
-- > example=
-- > if True
-- > then do
-- > liftIO $ print "world"
-- > liftIO $ undefined
-- >
-- > else liftIO $ print "not there"
--
-- Produce this trace:
--
-- @
-- \"hello\"
-- \"world\"
-- TraceExample.hs: TRACE (error in the last line):
-- .
-- main, Demos.TraceExample(Demos\TraceExample.hs): (23, 18)
-- main, Demos.TraceExample(Demos\TraceExample.hs): (26, 4)
-- example, Demos.TraceExample(Demos\TraceExample.hs): (30, 13)
-- example, Demos.TraceExample(Demos\TraceExample.hs): (32, 15)
-- exception: Prelude.undefined
-- @
-- TO DO: extend it for forward traces and test coverages
module Control.Monad.Supervisor.Trace(runTrace) where
import Control.Monad.State
import Control.Monad.Supervisor
import Control.Monad.Loc
import Control.Monad.Catch as CMC
import Control.Exception (SomeException)
import Data.List(intersperse)
type Trace= [String]
instance (MonadLoc m, Supervise Trace m, MonadCatch m)=> MonadLoc (Sup Trace m) where
withLoc loc (Sup f) = Sup $ do
withLoc loc $ do
r <- f `CMC.catch` handler1
trace <- get
case trace of
[] -> return r -- all ok
trace -> put (loc:trace) >> return r -- is going back with a trace, we add one more line
return r
where
-- detected failure, add the first line of trace with the error, init execution back
handler1 (e :: SomeException)= put ["exception: " ++show e] >> return Backward
-- | Execute an Supervisor computation and raise an error with a trace when an uncaugh exception
-- is raised. It is necessary to preprocess the file with the monadloc-pp preprocessor.
--
-- Otherwise, it produces the same error with no trace.
runTrace :: Supervise [String] m => Sup [String] m a -> m (Control a)
runTrace f= runSup f1
where
f1= printBackTrace >> f
printBackTrace= do
s <- get
case s of
[] -> breturn()
tr -> error (disp tr)
where
disp tr= "TRACE (error in the last line):\n\n" ++(concat $ intersperse "\n" tr)
---- A less polimorphic version of runTrace. It assumes a state monad for the sole purpose
---- of capturing traces
--runTraceState :: Monad m => Sup (StateT [String] m) a -> m (Control a)
--runTraceState f= evalStateT (runTrace f) []