packages feed

hoq-0.2: src/TypeChecking/Monad/Warn.hs

{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}

module TypeChecking.Monad.Warn
    ( WarnT, warn, runWarnT
    , throwError, catchError
    , catchErrorBy
    , forW, throwErrors
    , mapWarnT
    ) where

import Control.Monad
import Control.Monad.Fix
import Control.Monad.Trans
import Control.Monad.Error.Class
import Control.Applicative
import Data.Monoid hiding ((<>))
import Data.Maybe
import Data.Either

newtype WarnT w m a = WarnT { runWarnT :: m (w, Maybe a) }

instance Functor m => Functor (WarnT w m) where
    fmap f (WarnT m) = WarnT $ fmap (\(w, ma) -> (w, fmap f ma)) m

instance (Monoid w, Applicative m) => Applicative (WarnT w m) where
    pure a = WarnT $ pure (mempty, Just a)
    WarnT f <*> WarnT a = WarnT $ (\(w1, mf) (w2, ma) -> (w1 `mappend` w2, mf <*> ma)) <$> f <*> a

instance (Monoid w, Monad m) => Monad (WarnT w m) where
    return a      = WarnT $ return (mempty, Just a)
    WarnT m >>= k = WarnT $ m >>= \(w, ma) -> case ma of
        Nothing -> return (w, Nothing)
        Just a  -> do
            (w', mb) <- runWarnT (k a)
            return (w `mappend` w', mb)

instance (Monoid w, Monad m) => MonadError w (WarnT w m) where
    throwError w = WarnT $ return (w, Nothing)
    catchError (WarnT m) h = WarnT $ m >>= \(w, ma) -> case ma of
        Nothing -> runWarnT (h w)
        Just _  -> return (w, ma)

instance Monoid w => MonadTrans (WarnT w) where
    lift m = WarnT $ liftM (\a -> (mempty, Just a)) m

instance (Monoid w, MonadIO m) => MonadIO (WarnT w m) where
    liftIO m = WarnT $ liftM (\a -> (mempty, Just a)) (liftIO m)

instance (Monoid w, MonadFix m) => MonadFix (WarnT w m) where
    mfix f = WarnT $ mfix $ \ ~(_, ~(Just a)) -> runWarnT (f a)

warn :: Monad m => w -> WarnT w m ()
warn w = WarnT $ return (w, Just ())

forW :: (Monoid w, Monad m) => [a] -> (a -> WarnT w m (Maybe b)) -> WarnT w m [b]
forW as k = liftM catMaybes $ forM as $ \a -> k a `catchError` \w -> warn w >> return Nothing

throwErrors :: Monad m => [w] -> WarnT [w] m ()
throwErrors [] = return ()
throwErrors ws = throwError ws

catchErrorBy :: Monad m => (w -> Bool) -> WarnT [w] m a -> ([w] -> WarnT [w] m a) -> WarnT [w] m a
catchErrorBy p m h = WarnT $ runWarnT m >>= \(errs, ma) ->
    case partitionEithers $ map (\err -> if p err then Left err else Right err) errs of
        ([],_) -> return (errs,ma)
        (errs1,errs2) -> runWarnT $ warn errs2 >> h errs1

mapWarnT :: Monad m => (w -> w') -> WarnT w m a -> WarnT w' m a
mapWarnT f (WarnT m) = WarnT $ liftM (\(w,a) -> (f w, a)) m