-- | Add tracing to the IO monad (see examples). -- -- [Usage] -- -- > {-# LANGUAGE RebindableSyntax #-} -- > import Prelude hiding (catch, (>>=), (>>), return, fail) -- > import Traced -- -- [Example] -- -- > test1 :: IO Int -- > test1 = do -- > Left x <- return (Left 1 :: Either Int Int) -- > putStrLn "Hello world" -- > Right y <- return (Left 2 :: Either Int Int) -- > return (x + y) -- -- outputs -- -- > Hello world -- > *** Exception: user error (Pattern match failure in do expression at Traced.hs:187:3-9) -- > Trace: -- > 0 Left 2 -- > 1 Left 1 -- -- [Guards] -- -- Use the following idiom instead of using 'Control.Monad.guard': -- -- > test2 :: IO Int -- > test2 = do -- > Left x <- return (Left 1 :: Either Int Int) -- > True <- return (x == 3) -- > return x -- -- The advantage of this idiom is that it gives you line number information when the guard fails: -- -- > *Traced> test2 -- > *** Exception: user error (Pattern match failure in do expression at Traced.hs:193:3-6) -- > Trace: -- > 0 Left 1 module Network.Transport.Tests.Traced ( MonadS(..) , return , (>>=) , (>>) , fail , ifThenElse , Showable(..) , Traceable(..) , traceShow ) where import Prelude hiding ( (>>=) , return , fail , (>>) #if ! MIN_VERSION_base(4,6,0) , catch #endif ) import qualified Prelude import Control.Exception (catches, Handler(..), SomeException, throwIO, Exception(..), IOException) import Control.Applicative ((<$>)) import Data.Typeable (Typeable) import Data.Maybe (catMaybes) import Data.ByteString (ByteString) import Data.Int (Int32, Int64) import Data.Word (Word32, Word64) import Control.Concurrent.MVar (MVar) -------------------------------------------------------------------------------- -- MonadS class -- -------------------------------------------------------------------------------- -- | Like 'Monad' but bind is only defined for 'Trace'able instances class MonadS m where returnS :: a -> m a bindS :: Traceable a => m a -> (a -> m b) -> m b failS :: String -> m a seqS :: m a -> m b -> m b -- | Redefinition of 'Prelude.>>=' (>>=) :: (MonadS m, Traceable a) => m a -> (a -> m b) -> m b (>>=) = bindS -- | Redefinition of 'Prelude.>>' (>>) :: MonadS m => m a -> m b -> m b (>>) = seqS -- | Redefinition of 'Prelude.return' return :: MonadS m => a -> m a return = returnS -- | Redefinition of 'Prelude.fail' fail :: MonadS m => String -> m a fail = failS -------------------------------------------------------------------------------- -- Trace typeclass (for adding elements to a trace -- -------------------------------------------------------------------------------- data Showable = forall a. Show a => Showable a instance Show Showable where show (Showable x) = show x mapShowable :: (forall a. Show a => a -> Showable) -> Showable -> Showable mapShowable f (Showable x) = f x traceShow :: Show a => a -> Maybe Showable traceShow = Just . Showable class Traceable a where trace :: a -> Maybe Showable instance (Traceable a, Traceable b) => Traceable (Either a b) where trace (Left x) = (mapShowable $ Showable . (Left :: forall c. c -> Either c ())) <$> trace x trace (Right y) = (mapShowable $ Showable . (Right :: forall c. c -> Either () c)) <$> trace y instance (Traceable a, Traceable b) => Traceable (a, b) where trace (x, y) = case (trace x, trace y) of (Nothing, Nothing) -> Nothing (Just t1, Nothing) -> traceShow t1 (Nothing, Just t2) -> traceShow t2 (Just t1, Just t2) -> traceShow (t1, t2) instance (Traceable a, Traceable b, Traceable c) => Traceable (a, b, c) where trace (x, y, z) = case (trace x, trace y, trace z) of (Nothing, Nothing, Nothing) -> Nothing (Just t1, Nothing, Nothing) -> traceShow t1 (Nothing, Just t2, Nothing) -> traceShow t2 (Just t1, Just t2, Nothing) -> traceShow (t1, t2) (Nothing, Nothing, Just t3) -> traceShow t3 (Just t1, Nothing, Just t3) -> traceShow (t1, t3) (Nothing, Just t2, Just t3) -> traceShow (t2, t3) (Just t1, Just t2, Just t3) -> traceShow (t1, t2, t3) instance Traceable a => Traceable (Maybe a) where trace Nothing = traceShow (Nothing :: Maybe ()) trace (Just x) = mapShowable (Showable . Just) <$> trace x instance Traceable a => Traceable [a] where trace = traceShow . catMaybes . map trace instance Traceable () where trace = const Nothing instance Traceable Int where trace = traceShow instance Traceable Int32 where trace = traceShow instance Traceable Int64 where trace = traceShow instance Traceable Word32 where trace = traceShow instance Traceable Word64 where trace = traceShow instance Traceable Bool where trace = const Nothing instance Traceable ByteString where trace = traceShow instance Traceable (MVar a) where trace = const Nothing instance Traceable [Char] where trace = traceShow instance Traceable IOException where trace = traceShow -------------------------------------------------------------------------------- -- IO instance for MonadS -- -------------------------------------------------------------------------------- data TracedException = TracedException [String] SomeException deriving Typeable instance Exception TracedException -- | Add tracing to 'IO' (see examples) instance MonadS IO where returnS = Prelude.return bindS = \x f -> x Prelude.>>= \a -> catches (f a) (traceHandlers a) failS = Prelude.fail seqS = (Prelude.>>) instance Show TracedException where show (TracedException ts ex) = show ex ++ "\nTrace:\n" ++ unlines (map (\(i, t) -> show i ++ "\t" ++ t) (zip ([0..] :: [Int]) (take 10 . reverse $ ts))) traceHandlers :: Traceable a => a -> [Handler b] traceHandlers a = case trace a of Nothing -> [ Handler $ \ex -> throwIO (ex :: SomeException) ] Just t -> [ Handler $ \(TracedException ts ex) -> throwIO $ TracedException (show t : ts) ex , Handler $ \ex -> throwIO $ TracedException [show t] (ex :: SomeException) ] -- | Definition of 'ifThenElse' for use with RebindableSyntax ifThenElse :: Bool -> a -> a -> a ifThenElse True x _ = x ifThenElse False _ y = y