{-# LANGUAGE PatternGuards #-}
module Control.Applicative.Error where

import Control.Applicative

-- | An error idiom.  Rather like the error monad, but collect all
-- | errors together 
data Failing a = Success a | Failure [ErrorMsg]
 deriving Show
type ErrorMsg = String

instance Functor Failing where
  fmap f (Failure fs) = Failure fs
  fmap f (Success a) = Success (f a)

instance Applicative Failing where
   pure = Success
   Failure msgs <*> Failure msgs' = Failure (msgs ++ msgs')
   Success _ <*> Failure msgs' = Failure msgs'
   Failure msgs' <*> Success _ = Failure msgs'
   Success f <*> Success x = Success (f x)

instance Alternative Failing where
  empty                       = Failure []
  (Success x) <|> _           = Success x
  _           <|> (Success y) = Success y
  (Failure x) <|> (Failure y) = Failure (x ++ y)

maybeRead :: Read a => String -> Maybe a
maybeRead s | [(i, "")] <- readsPrec 0 s = Just i
            | otherwise = Nothing

-- | Tries to read a value. Shows an error message when reading fails.
maybeRead' :: Read a => String -> String -> Failing a
maybeRead' s msg | Just x <- maybeRead s = Success x
                 | otherwise = Failure [msg]

-- | Tries to read an Integer
asInteger :: String -> Failing Integer
asInteger s = maybeRead' s (s ++ " is not a valid integer")

-- | Tries conversion to an enum
tryToEnum :: Enum a => Int -> Failing a
tryToEnum x | value <- toEnum x = Success value
            | otherwise         = Failure ["Conversion error"]