RichConditional-0.1.0.0: Control/RichConditional.hs
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE FlexibleInstances #-}
module Control.RichConditional (
TotalIf
, decide
, PartialIf
, indicate
, ifElse
, inCase
, ensure
, ensurePositive
, ensureNegative
) where
import Data.Maybe
import Data.Either
import Control.Monad
type Indication = Maybe
type Decision = Either
-- | Instances of this class provide a test on some type a. If it passes, it
-- gives a value of type b; otherwise, it gives Nothing.
class PartialIf a b where
indicate :: a -> Indication b
instance PartialIf (Maybe a) a where
indicate = id
-- | Instances of this class provide a test on some type a with an
-- alternative case for when it fails. If it passes, it gives a value of
-- type b; otherwise, it gives a value of type c.
class TotalIf a b c where
decide :: a -> Decision b c
instance TotalIf (Either a b) a b where
decide = id
-- | A conditional using a PartialIf instance. This is just like the
-- function 'maybe' which decomposes a Maybe, except that the Maybe
-- is produced through an PartialIf instance determined by the type of
-- the positive case.
inCase :: PartialIf a b => a -> (b -> c) -> c -> c
inCase x ifYes ifNo = maybe ifNo ifYes (indicate x)
-- | A conditional using a TotalIf instance. This is just like the
-- function 'either' which decomposes an Either, except that the Either
-- is produced through a TotalIf instance determined by the types of
-- the cases.
ifElse :: TotalIf a b c => a -> (b -> d) -> (c -> d) -> d
ifElse x ifYes ifNo = either ifYes ifNo (decide x)
-- | Like 'guard' but with a bonus: if the condition passes, you actually get
-- a hold of some new information.
ensure :: (Monad m, MonadPlus m, PartialIf a b) => a -> m b
ensure x = inCase x return mzero
-- | Like 'ensure' but for the positive case (Left) of a TotalIf.
-- Requires a proxy on the negative type in order to disambiguate.
ensurePositive
:: forall m a b c u .
( Monad m
, MonadPlus m
, TotalIf a b c
)
=> a
-> u c
-> m b
ensurePositive x _ =
let typedConstMzero = const mzero :: c -> m b
in ifElse x return typedConstMzero
-- | Like 'ensure' but for the negative case (Right) of a TotalIf.
-- Requires a proxy on the positive type in order to disambiguate.
ensureNegative
:: forall m a b c u .
( Monad m
, MonadPlus m
, TotalIf a b c
)
=> a
-> u b
-> m c
ensureNegative x _ =
let typedConstMzero = const mzero :: b -> m c
in ifElse x typedConstMzero return