{- |
This module is currently not in use and may be considered a design study.
Warning monad is like 'Control.Monad.Writer.Writer' monad,
it can be used to record exceptions that do not break program flow.

TODO:

* Better name for 'Warnable'
-}
module Control.Monad.Exception.Warning where

import qualified Control.Monad.Exception.Synchronous as Sync

import Control.Applicative (Applicative(pure, (<*>)))
import Control.Monad (mplus)
import Data.Maybe (catMaybes)


-- * Plain monad

{- |
Contains a value and
possibly warnings that were generated while the computation of that value.
-}
data Warnable e a =
   Warnable [Maybe e] a


{- |
Convert an exception to a warning.
-}
fromException :: a -> Sync.Exceptional e a -> Warnable e a
fromException deflt x =
{- Here the list item can only be constructed after the constructor of x is known
   case x of
      Sync.Success y   -> Warnable [Nothing] y
      Sync.Exception e -> Warnable [Just e] deflt
-}
   let (e,y) =
           case x of
              Sync.Success y0   -> (Nothing, y0)
              Sync.Exception e0 -> (Just e0, deflt)
   in  Warnable [e] y

fromExceptionNull :: Sync.Exceptional e () -> Warnable e ()
fromExceptionNull = fromException ()

toException :: ([e0] -> e1) -> Warnable e0 a -> Sync.Exceptional e1 a
toException summarize x =
   case x of
      Warnable mes y ->
         case catMaybes mes of
            [] -> Sync.Success y
            es -> Sync.Exception (summarize es)



warn :: e -> Warnable e ()
warn e = Warnable [Just e] ()



instance Functor (Warnable e) where
   fmap f x =
      case x of
         Warnable e a -> Warnable e (f a)

instance Applicative (Warnable e) where
   pure = Warnable [] -- [Nothing]?
   f <*> x =
      case f of
         Warnable e0 g ->
            case x of
               Warnable e1 y -> Warnable (mplus e0 e1) (g y)

instance Monad (Warnable e) where
   return = Warnable [] -- [Nothing]?
   fail _msg =
      Warnable
         [Just (error "Warning.fail exception")]
         (error "Warning.fail result")
   x >>= f =
      case x of
         Warnable e0 y ->
            case f y of
               Warnable e1 z -> Warnable (e0 ++ e1) z


-- * Monad transformer

newtype WarnableT e m a =
   WarnableT {runWarnableT :: m (Warnable e a)}


fromSynchronousT :: Functor m =>
   a -> Sync.ExceptionalT e m a -> WarnableT e m a
fromSynchronousT deflt (Sync.ExceptionalT mx) =
   WarnableT $ fmap (fromException deflt) mx



warnT :: (Monad m) =>
   e -> WarnableT e m ()
warnT = WarnableT . return . warn



instance Functor m => Functor (WarnableT e m) where
   fmap f (WarnableT x) =
      WarnableT (fmap (fmap f) x)

instance Applicative m => Applicative (WarnableT e m) where
   pure = WarnableT . pure . pure
   WarnableT f <*> WarnableT x =
      WarnableT (fmap (<*>) f <*> x)

instance Monad m => Monad (WarnableT e m) where
   return = WarnableT . return . return
   x0 >>= f =
      WarnableT $
      do Warnable ex x <- runWarnableT x0
         Warnable ey y <- runWarnableT (f x)
         return $ Warnable (ex ++ ey) y
