grisette-0.7.0.0: src/Grisette/Internal/Core/Data/Class/TryMerge.hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE ConstraintKinds #-}
-- |
-- Module : Grisette.Internal.Core.Data.Class.TryMerge
-- Copyright : (c) Sirui Lu 2023-2024
-- License : BSD-3-Clause (see the LICENSE file)
--
-- Maintainer : siruilu@cs.washington.edu
-- Stability : Experimental
-- Portability : GHC only
module Grisette.Internal.Core.Data.Class.TryMerge
( TryMerge (..),
tryMerge,
MonadTryMerge,
mrgSingle,
mrgSingleWithStrategy,
)
where
import Control.Monad.Cont (ContT (ContT))
import Control.Monad.Except (ExceptT (ExceptT))
import Control.Monad.Identity
( Identity,
IdentityT (IdentityT),
)
import qualified Control.Monad.RWS.Lazy as RWSLazy
import qualified Control.Monad.RWS.Strict as RWSStrict
import Control.Monad.Reader (ReaderT (ReaderT))
import qualified Control.Monad.State.Lazy as StateLazy
import qualified Control.Monad.State.Strict as StateStrict
import Control.Monad.Trans.Maybe (MaybeT (MaybeT))
import qualified Control.Monad.Writer.Lazy as WriterLazy
import qualified Control.Monad.Writer.Strict as WriterStrict
import Data.Functor.Sum (Sum (InL, InR))
import qualified Data.Monoid as Monoid
import Grisette.Internal.Core.Data.Class.Mergeable
( Mergeable (rootStrategy),
Mergeable1 (liftRootStrategy),
Mergeable2 (liftRootStrategy2),
Mergeable3 (liftRootStrategy3),
MergingStrategy,
)
-- $setup
-- >>> import Grisette.Core
-- >>> import Grisette.SymPrim
-- | A class for containers that may or may not be merged.
--
-- If the container is capable of multi-path execution, then the
-- `tryMergeWithStrategy` function should merge the paths according to the
-- supplied strategy.
--
-- If the container is not capable of multi-path execution, then the
-- `tryMergeWithStrategy` function should be equivalent to `id`.
--
-- Note that this will not necessarily do a recursive merge for the elements.
class TryMerge m where
tryMergeWithStrategy :: MergingStrategy a -> m a -> m a
-- | Try to merge the container with the root strategy.
tryMerge :: (TryMerge m, Mergeable a) => m a -> m a
tryMerge = tryMergeWithStrategy rootStrategy
{-# INLINE tryMerge #-}
-- | Wrap a value in the applicative functor and capture the 'Mergeable'
-- knowledge.
--
-- >>> mrgSingleWithStrategy rootStrategy "a" :: Union SymInteger
-- {a}
--
-- __Note:__ Be careful to call this directly from your code.
-- The supplied merge strategy should be consistent with the type's root merge
-- strategy, or some internal invariants would be broken and the program can
-- crash.
--
-- This function is to be called when the 'Mergeable' constraint can not be
-- resolved, e.g., the merge strategy for the contained type is given with
-- 'Mergeable1'. In other cases, 'Grisette.Lib.Control.Applicative.mrgPure'
-- is usually a better alternative.
mrgSingleWithStrategy ::
(TryMerge m, Applicative m) =>
MergingStrategy a ->
a ->
m a
mrgSingleWithStrategy strategy = tryMergeWithStrategy strategy . pure
{-# INLINE mrgSingleWithStrategy #-}
-- | Wrap a value in the applicative functor and propagate the type's root merge
-- strategy.
--
-- Equivalent to @'mrgSingleWithStrategy' 'rootStrategy'@.
--
-- >>> mrgSingle "a" :: Union SymInteger
-- {a}
mrgSingle :: (TryMerge m, Applicative m, Mergeable a) => a -> m a
mrgSingle = mrgSingleWithStrategy rootStrategy
{-# INLINE mrgSingle #-}
instance (TryMerge m) => TryMerge (MaybeT m) where
tryMergeWithStrategy strategy (MaybeT ma) =
MaybeT $ tryMergeWithStrategy (liftRootStrategy strategy) ma
{-# INLINE tryMergeWithStrategy #-}
instance (Mergeable e, TryMerge m) => TryMerge (ExceptT e m) where
tryMergeWithStrategy strategy (ExceptT ma) =
ExceptT $ tryMergeWithStrategy (liftRootStrategy strategy) ma
{-# INLINE tryMergeWithStrategy #-}
instance (TryMerge m) => TryMerge (ReaderT r m) where
tryMergeWithStrategy strategy (ReaderT f) =
ReaderT $ \v -> tryMergeWithStrategy strategy $ f v
{-# INLINE tryMergeWithStrategy #-}
instance (Mergeable s, TryMerge m) => TryMerge (StateLazy.StateT s m) where
tryMergeWithStrategy strategy (StateLazy.StateT f) =
StateLazy.StateT $
\s -> tryMergeWithStrategy (liftRootStrategy2 strategy rootStrategy) (f s)
{-# INLINE tryMergeWithStrategy #-}
instance (Mergeable s, TryMerge m) => TryMerge (StateStrict.StateT s m) where
tryMergeWithStrategy strategy (StateStrict.StateT f) =
StateStrict.StateT $
\s -> tryMergeWithStrategy (liftRootStrategy2 strategy rootStrategy) (f s)
{-# INLINE tryMergeWithStrategy #-}
instance
(Monoid w, Mergeable w, TryMerge m) =>
TryMerge (WriterLazy.WriterT w m)
where
tryMergeWithStrategy strategy (WriterLazy.WriterT f) =
WriterLazy.WriterT $
tryMergeWithStrategy (liftRootStrategy2 strategy rootStrategy) f
{-# INLINE tryMergeWithStrategy #-}
instance
(Monoid w, Mergeable w, TryMerge m) =>
TryMerge (WriterStrict.WriterT w m)
where
tryMergeWithStrategy strategy (WriterStrict.WriterT f) =
WriterStrict.WriterT $
tryMergeWithStrategy (liftRootStrategy2 strategy rootStrategy) f
{-# INLINE tryMergeWithStrategy #-}
instance
(Monoid w, Mergeable w, Mergeable s, TryMerge m) =>
TryMerge (RWSStrict.RWST r w s m)
where
tryMergeWithStrategy strategy (RWSStrict.RWST f) =
RWSStrict.RWST $
\r s ->
tryMergeWithStrategy
(liftRootStrategy3 strategy rootStrategy rootStrategy)
(f r s)
{-# INLINE tryMergeWithStrategy #-}
instance
(Monoid w, Mergeable w, Mergeable s, TryMerge m) =>
TryMerge (RWSLazy.RWST r w s m)
where
tryMergeWithStrategy strategy (RWSLazy.RWST f) =
RWSLazy.RWST $
\r s ->
tryMergeWithStrategy
(liftRootStrategy3 strategy rootStrategy rootStrategy)
(f r s)
{-# INLINE tryMergeWithStrategy #-}
instance (TryMerge m) => TryMerge (IdentityT m) where
tryMergeWithStrategy strategy (IdentityT ma) =
IdentityT $ tryMergeWithStrategy strategy ma
{-# INLINE tryMergeWithStrategy #-}
instance (TryMerge m, Mergeable r) => TryMerge (ContT r m) where
tryMergeWithStrategy _ (ContT ma) =
ContT $ \c -> tryMergeWithStrategy rootStrategy (ma c)
{-# INLINE tryMergeWithStrategy #-}
-- | Alias for a monad type that has 'TryMerge'.
type MonadTryMerge f = (TryMerge f, Monad f)
#define TRYMERGE_ID(T) \
instance TryMerge (T) where { \
tryMergeWithStrategy _ = id; {-# INLINE tryMergeWithStrategy #-} \
}
#if 1
TRYMERGE_ID(Either a)
TRYMERGE_ID(Maybe)
TRYMERGE_ID(Identity)
TRYMERGE_ID([])
TRYMERGE_ID((,) a)
TRYMERGE_ID((,,) a b)
TRYMERGE_ID((,,,) a b c)
TRYMERGE_ID((,,,,) a b c d)
TRYMERGE_ID((,,,,,) a b c d e)
TRYMERGE_ID((,,,,,,) a b c d e f)
TRYMERGE_ID((,,,,,,,) a b c d e f g)
TRYMERGE_ID((,,,,,,,,) a b c d e f g h)
#endif
instance (TryMerge f, TryMerge g) => TryMerge (Sum f g) where
tryMergeWithStrategy strategy (InL fa) =
InL $ tryMergeWithStrategy strategy fa
tryMergeWithStrategy strategy (InR fa) =
InR $ tryMergeWithStrategy strategy fa
instance TryMerge Monoid.Sum where
tryMergeWithStrategy _ = id
{-# INLINE tryMergeWithStrategy #-}