grisette-0.3.0.0: src/Grisette/Core/Control/Monad/CBMCExcept.hs
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveLift #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE Trustworthy #-}
{-# LANGUAGE UndecidableInstances #-}
-- |
-- Module : Grisette.Core.Control.Monad.CBMCExcept
-- Copyright : (c) Sirui Lu 2021-2023
-- License : BSD-3-Clause (see the LICENSE file)
--
-- Maintainer : siruilu@cs.washington.edu
-- Stability : Experimental
-- Portability : GHC only
module Grisette.Core.Control.Monad.CBMCExcept
( -- * CBMC-like error handling
CBMCEither (..),
CBMCExceptT (..),
cbmcExcept,
mapCBMCExceptT,
withCBMCExceptT,
OrigExcept.MonadError (..),
)
where
import Control.Applicative
import Control.DeepSeq
import Control.Monad
import qualified Control.Monad.Except as OrigExcept
import qualified Control.Monad.Fail as Fail
import Control.Monad.Fix
import Control.Monad.Trans
import Control.Monad.Zip
import Data.Functor.Classes
import Data.Functor.Contravariant
import Data.Hashable
import GHC.Generics
import Grisette.Core.Data.Class.Bool
import Grisette.Core.Data.Class.Evaluate
import Grisette.Core.Data.Class.ExtractSymbolics
import Grisette.Core.Data.Class.GenSym
import Grisette.Core.Data.Class.Mergeable
import Grisette.Core.Data.Class.SOrd
import Grisette.Core.Data.Class.SimpleMergeable
import Grisette.Core.Data.Class.Solver
import Grisette.Core.Data.Class.ToCon
import Grisette.Core.Data.Class.ToSym
import Language.Haskell.TH.Syntax (Lift)
import Unsafe.Coerce
-- | A wrapper type for 'Either'. Uses different merging strategies.
newtype CBMCEither a b = CBMCEither {runCBMCEither :: Either a b}
deriving newtype (Eq, Eq1, Ord, Ord1, Read, Read1, Show, Show1, Functor, Applicative, Monad, Hashable, NFData)
deriving stock (Generic, Lift)
deriving newtype instance (SEq e, SEq a) => SEq (CBMCEither e a)
deriving newtype instance (EvaluateSym a, EvaluateSym b) => EvaluateSym (CBMCEither a b)
deriving newtype instance
(ExtractSymbolics a, ExtractSymbolics b) =>
ExtractSymbolics (CBMCEither a b)
instance
( GenSymSimple a a,
Mergeable a,
GenSymSimple b b,
Mergeable b
) =>
GenSym (CBMCEither a b) (CBMCEither a b)
instance
( GenSymSimple a a,
GenSymSimple b b
) =>
GenSymSimple (CBMCEither a b) (CBMCEither a b)
where
simpleFresh = derivedSameShapeSimpleFresh
instance
(GenSym () a, Mergeable a, GenSym () b, Mergeable b) =>
GenSym () (CBMCEither a b)
where
fresh = derivedNoSpecFresh
deriving newtype instance (SOrd a, SOrd b) => SOrd (CBMCEither a b)
deriving newtype instance (ToCon e1 e2, ToCon a1 a2) => ToCon (Either e1 a1) (CBMCEither e2 a2)
instance (ToCon e1 e2, ToCon a1 a2) => ToCon (CBMCEither e1 a1) (CBMCEither e2 a2) where
toCon (CBMCEither a) = CBMCEither <$> toCon a
instance (ToCon e1 e2, ToCon a1 a2) => ToCon (CBMCEither e1 a1) (Either e2 a2) where
toCon (CBMCEither a) = toCon a
deriving newtype instance (ToSym e1 e2, ToSym a1 a2) => ToSym (Either e1 a1) (CBMCEither e2 a2)
instance (ToSym e1 e2, ToSym a1 a2) => ToSym (CBMCEither e1 a1) (CBMCEither e2 a2) where
toSym (CBMCEither a) = CBMCEither $ toSym a
instance (ToSym e1 e2, ToSym a1 a2) => ToSym (CBMCEither e1 a1) (Either e2 a2) where
toSym (CBMCEither a) = toSym a
data EitherIdx idx = L idx | R deriving (Eq, Ord, Show)
instance (Mergeable e, Mergeable a) => Mergeable (CBMCEither e a) where
rootStrategy = rootStrategy1
instance (Mergeable e) => Mergeable1 (CBMCEither e) where
liftRootStrategy ms = case rootStrategy of
SimpleStrategy m ->
SortedStrategy
( \(CBMCEither e) -> case e of
Left _ -> False
Right _ -> True
)
( \case
False -> SimpleStrategy $
\cond (CBMCEither le) (CBMCEither re) -> case (le, re) of
(Left l, Left r) -> CBMCEither $ Left $ m cond l r
_ -> error "impossible"
True -> wrapStrategy ms (CBMCEither . Right) (\case (CBMCEither (Right x)) -> x; _ -> error "impossible")
)
NoStrategy ->
SortedStrategy
( \(CBMCEither e) -> case e of
Left _ -> False
Right _ -> True
)
( \case
False -> NoStrategy
True -> wrapStrategy ms (CBMCEither . Right) (\case (CBMCEither (Right x)) -> x; _ -> error "impossible")
)
SortedStrategy idx sub ->
SortedStrategy
( \(CBMCEither e) -> case e of
Left v -> L $ idx v
Right _ -> R
)
( \case
L i -> wrapStrategy (sub i) (CBMCEither . Left) (\case (CBMCEither (Left x)) -> x; _ -> error "impossible")
R -> wrapStrategy ms (CBMCEither . Right) (\case (CBMCEither (Right x)) -> x; _ -> error "impossible")
)
cbmcEither :: forall a c b. (a -> c) -> (b -> c) -> CBMCEither a b -> c
cbmcEither l r v = either l r (unsafeCoerce v)
-- | Wrap an 'Either' value in 'CBMCExceptT'
cbmcExcept :: (Monad m) => Either e a -> CBMCExceptT e m a
cbmcExcept m = CBMCExceptT (return $ CBMCEither m)
-- | Map the error and values in a 'CBMCExceptT'
mapCBMCExceptT :: (m (Either e a) -> n (Either e' b)) -> CBMCExceptT e m a -> CBMCExceptT e' n b
mapCBMCExceptT f m = CBMCExceptT $ (unsafeCoerce . f . unsafeCoerce) (runCBMCExceptT m)
-- | Map the error in a 'CBMCExceptT'
withCBMCExceptT :: (Functor m) => (e -> e') -> CBMCExceptT e m a -> CBMCExceptT e' m a
withCBMCExceptT f = mapCBMCExceptT $ fmap $ either (Left . f) Right
-- | Similar to 'ExceptT', but with different error handling mechanism.
newtype CBMCExceptT e m a = CBMCExceptT {runCBMCExceptT :: m (CBMCEither e a)} deriving stock (Generic, Generic1)
instance (Eq e, Eq1 m) => Eq1 (CBMCExceptT e m) where
liftEq eq (CBMCExceptT x) (CBMCExceptT y) = liftEq (liftEq eq) x y
{-# INLINE liftEq #-}
instance (Ord e, Ord1 m) => Ord1 (CBMCExceptT e m) where
liftCompare comp (CBMCExceptT x) (CBMCExceptT y) =
liftCompare (liftCompare comp) x y
{-# INLINE liftCompare #-}
instance (Read e, Read1 m) => Read1 (CBMCExceptT e m) where
liftReadsPrec rp rl =
readsData $
readsUnaryWith (liftReadsPrec rp' rl') "CBMCExceptT" CBMCExceptT
where
rp' = liftReadsPrec rp rl
rl' = liftReadList rp rl
instance (Show e, Show1 m) => Show1 (CBMCExceptT e m) where
liftShowsPrec sp sl d (CBMCExceptT m) =
showsUnaryWith (liftShowsPrec sp' sl') "CBMCExceptT" d m
where
sp' = liftShowsPrec sp sl
sl' = liftShowList sp sl
instance (Eq e, Eq1 m, Eq a) => Eq (CBMCExceptT e m a) where
(==) = eq1
instance (Ord e, Ord1 m, Ord a) => Ord (CBMCExceptT e m a) where
compare = compare1
instance (Read e, Read1 m, Read a) => Read (CBMCExceptT e m a) where
readsPrec = readsPrec1
instance (Show e, Show1 m, Show a) => Show (CBMCExceptT e m a) where
showsPrec = showsPrec1
instance (Functor m) => Functor (CBMCExceptT e m) where
fmap f = CBMCExceptT . fmap (fmap f) . runCBMCExceptT
{-# INLINE fmap #-}
instance (Foldable f) => Foldable (CBMCExceptT e f) where
foldMap f (CBMCExceptT a) = foldMap (cbmcEither (const mempty) f) a
{-# INLINE foldMap #-}
instance (Traversable f) => Traversable (CBMCExceptT e f) where
traverse f (CBMCExceptT a) =
CBMCExceptT <$> traverse (cbmcEither (pure . CBMCEither . Left) (fmap (CBMCEither . Right) . f)) a
{-# INLINE traverse #-}
instance (Functor m, Monad m) => Applicative (CBMCExceptT e m) where
pure a = CBMCExceptT $ return (CBMCEither . Right $ a)
{-# INLINE pure #-}
CBMCExceptT f <*> CBMCExceptT v = CBMCExceptT $ do
mf <- f
case mf of
CBMCEither (Left e) -> return (CBMCEither . Left $ e)
CBMCEither (Right k) -> do
mv <- v
case mv of
CBMCEither (Left e) -> return (CBMCEither . Left $ e)
CBMCEither (Right x) -> return (CBMCEither . Right $ k x)
{-# INLINEABLE (<*>) #-}
m *> k = m >> k
{-# INLINE (*>) #-}
instance (Functor m, Monad m, Monoid e) => Alternative (CBMCExceptT e m) where
empty = CBMCExceptT $ return (CBMCEither . Left $ mempty)
{-# INLINE empty #-}
CBMCExceptT mx <|> CBMCExceptT my = CBMCExceptT $ do
ex <- mx
case ex of
CBMCEither (Left e) -> fmap (cbmcEither (CBMCEither . Left . mappend e) (CBMCEither . Right)) my
CBMCEither (Right x) -> return (CBMCEither . Right $ x)
{-# INLINEABLE (<|>) #-}
instance (Monad m) => Monad (CBMCExceptT e m) where
m >>= k = CBMCExceptT $ do
a <- runCBMCExceptT m
case a of
CBMCEither (Left e) -> return (CBMCEither $ Left e)
CBMCEither (Right x) -> runCBMCExceptT (k x)
{-# INLINE (>>=) #-}
instance (Fail.MonadFail m) => Fail.MonadFail (CBMCExceptT e m) where
fail = CBMCExceptT . Fail.fail
{-# INLINE fail #-}
instance (Monad m, Monoid e) => MonadPlus (CBMCExceptT e m) where
mzero = CBMCExceptT $ return (CBMCEither $ Left mempty)
{-# INLINE mzero #-}
CBMCExceptT mx `mplus` CBMCExceptT my = CBMCExceptT $ do
ex <- mx
case ex of
CBMCEither (Left e) -> fmap (cbmcEither (CBMCEither . Left . mappend e) (CBMCEither . Right)) my
CBMCEither (Right x) -> return (CBMCEither $ Right x)
{-# INLINEABLE mplus #-}
instance (MonadFix m) => MonadFix (CBMCExceptT e m) where
mfix f = CBMCExceptT (mfix (runCBMCExceptT . f . cbmcEither (const bomb) id))
where
bomb = error "mfix (CBMCExceptT): inner computation returned Left value"
{-# INLINE mfix #-}
instance MonadTrans (CBMCExceptT e) where
lift = CBMCExceptT . fmap (CBMCEither . Right)
{-# INLINE lift #-}
instance (MonadIO m) => MonadIO (CBMCExceptT e m) where
liftIO = lift . liftIO
{-# INLINE liftIO #-}
instance (MonadZip m) => MonadZip (CBMCExceptT e m) where
mzipWith f (CBMCExceptT a) (CBMCExceptT b) = CBMCExceptT $ mzipWith (liftA2 f) a b
{-# INLINE mzipWith #-}
instance (Contravariant m) => Contravariant (CBMCExceptT e m) where
contramap f = CBMCExceptT . contramap (fmap f) . runCBMCExceptT
{-# INLINE contramap #-}
throwE :: (Monad m) => e -> CBMCExceptT e m a
throwE = CBMCExceptT . return . CBMCEither . Left
{-# INLINE throwE #-}
catchE ::
(Monad m) =>
CBMCExceptT e m a ->
(e -> CBMCExceptT e' m a) ->
CBMCExceptT e' m a
m `catchE` h = CBMCExceptT $ do
a <- runCBMCExceptT m
case a of
CBMCEither (Left l) -> runCBMCExceptT (h l)
CBMCEither (Right r) -> return (CBMCEither . Right $ r)
{-# INLINE catchE #-}
instance (Monad m) => OrigExcept.MonadError e (CBMCExceptT e m) where
throwError = throwE
{-# INLINE throwError #-}
catchError = catchE
{-# INLINE catchError #-}
instance (SEq (m (CBMCEither e a))) => SEq (CBMCExceptT e m a) where
(CBMCExceptT a) ==~ (CBMCExceptT b) = a ==~ b
{-# INLINE (==~) #-}
instance (EvaluateSym (m (CBMCEither e a))) => EvaluateSym (CBMCExceptT e m a) where
evaluateSym fillDefault model (CBMCExceptT v) = CBMCExceptT $ evaluateSym fillDefault model v
{-# INLINE evaluateSym #-}
instance
(ExtractSymbolics (m (CBMCEither e a))) =>
ExtractSymbolics (CBMCExceptT e m a)
where
extractSymbolics (CBMCExceptT v) = extractSymbolics v
instance
(Mergeable1 m, Mergeable e, Mergeable a) =>
Mergeable (CBMCExceptT e m a)
where
rootStrategy = wrapStrategy rootStrategy1 CBMCExceptT runCBMCExceptT
{-# INLINE rootStrategy #-}
instance (Mergeable1 m, Mergeable e) => Mergeable1 (CBMCExceptT e m) where
liftRootStrategy m = wrapStrategy (liftRootStrategy (liftRootStrategy m)) CBMCExceptT runCBMCExceptT
{-# INLINE liftRootStrategy #-}
instance
{-# OVERLAPPABLE #-}
( GenSym spec (m (CBMCEither a b)),
Mergeable1 m,
Mergeable a,
Mergeable b
) =>
GenSym spec (CBMCExceptT a m b)
where
fresh v = do
x <- fresh v
return $ merge . fmap CBMCExceptT $ x
instance
{-# OVERLAPPABLE #-}
( GenSymSimple spec (m (CBMCEither a b))
) =>
GenSymSimple spec (CBMCExceptT a m b)
where
simpleFresh v = CBMCExceptT <$> simpleFresh v
instance
{-# OVERLAPPING #-}
( GenSymSimple (m (CBMCEither e a)) (m (CBMCEither e a))
) =>
GenSymSimple (CBMCExceptT e m a) (CBMCExceptT e m a)
where
simpleFresh (CBMCExceptT v) = CBMCExceptT <$> simpleFresh v
instance
{-# OVERLAPPING #-}
( GenSymSimple (m (CBMCEither e a)) (m (CBMCEither e a)),
Mergeable1 m,
Mergeable e,
Mergeable a
) =>
GenSym (CBMCExceptT e m a) (CBMCExceptT e m a)
instance
(UnionLike m, Mergeable e, Mergeable a) =>
SimpleMergeable (CBMCExceptT e m a)
where
mrgIte = mrgIf
{-# INLINE mrgIte #-}
instance
(UnionLike m, Mergeable e) =>
SimpleMergeable1 (CBMCExceptT e m)
where
liftMrgIte m = mrgIfWithStrategy (SimpleStrategy m)
{-# INLINE liftMrgIte #-}
instance
(UnionLike m, Mergeable e) =>
UnionLike (CBMCExceptT e m)
where
mergeWithStrategy s (CBMCExceptT v) = CBMCExceptT $ mergeWithStrategy (liftRootStrategy s) v
{-# INLINE mergeWithStrategy #-}
mrgIfWithStrategy s cond (CBMCExceptT t) (CBMCExceptT f) = CBMCExceptT $ mrgIfWithStrategy (liftRootStrategy s) cond t f
{-# INLINE mrgIfWithStrategy #-}
single = CBMCExceptT . single . return
{-# INLINE single #-}
unionIf cond (CBMCExceptT l) (CBMCExceptT r) = CBMCExceptT $ unionIf cond l r
{-# INLINE unionIf #-}
instance (SOrd (m (CBMCEither e a))) => SOrd (CBMCExceptT e m a) where
(CBMCExceptT l) <=~ (CBMCExceptT r) = l <=~ r
(CBMCExceptT l) <~ (CBMCExceptT r) = l <~ r
(CBMCExceptT l) >=~ (CBMCExceptT r) = l >=~ r
(CBMCExceptT l) >~ (CBMCExceptT r) = l >~ r
symCompare (CBMCExceptT l) (CBMCExceptT r) = symCompare l r
instance
(ToCon (m1 (CBMCEither e1 a)) (m2 (CBMCEither e2 b))) =>
ToCon (CBMCExceptT e1 m1 a) (CBMCExceptT e2 m2 b)
where
toCon (CBMCExceptT v) = CBMCExceptT <$> toCon v
instance
(ToCon (m1 (CBMCEither e1 a)) (Either e2 b)) =>
ToCon (CBMCExceptT e1 m1 a) (Either e2 b)
where
toCon (CBMCExceptT v) = toCon v
instance
(ToSym (m1 (CBMCEither e1 a)) (m2 (CBMCEither e2 b))) =>
ToSym (CBMCExceptT e1 m1 a) (CBMCExceptT e2 m2 b)
where
toSym (CBMCExceptT v) = CBMCExceptT $ toSym v
instance
(Monad u, UnionLike u, Mergeable e, Mergeable v) =>
UnionWithExcept (CBMCExceptT e u v) u e v
where
extractUnionExcept = merge . fmap runCBMCEither . runCBMCExceptT