grisette-0.5.0.0: src/Grisette/Lib/Control/Monad.hs
{-# LANGUAGE ApplicativeDo #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE Trustworthy #-}
-- |
-- Module : Grisette.Lib.Control.Monad
-- Copyright : (c) Sirui Lu 2021-2024
-- License : BSD-3-Clause (see the LICENSE file)
--
-- Maintainer : siruilu@cs.washington.edu
-- Stability : Experimental
-- Portability : GHC only
module Grisette.Lib.Control.Monad
( -- * Functor and Monad classes
mrgFmap,
(.<$),
mrgReturnWithStrategy,
mrgBindWithStrategy,
mrgReturn,
(.>>=),
(.>>),
mrgFail,
mrgMzero,
mrgMplus,
-- * Functions
-- ** Basic 'Monad' functions
mrgMapM,
mrgMapM_,
mrgForM,
mrgForM_,
mrgSequence,
mrgSequence_,
(.=<<),
(.>=>),
(.<=<),
mrgForever,
mrgVoid,
-- ** Generalisations of list functions
mrgJoin,
mrgMsum,
mrgMfilter,
symMfilter,
mrgFilterM,
symFilterM,
mrgMapAndUnzipM,
mrgZipWithM,
mrgZipWithM_,
mrgFoldM,
mrgFoldM_,
mrgReplicateM,
symReplicateM,
mrgReplicateM_,
symReplicateM_,
-- ** Conditional execution of monadic expressions
mrgGuard,
symGuard,
mrgWhen,
symWhen,
mrgUnless,
symUnless,
-- ** Monadic lifting operators
mrgLiftM,
mrgLiftM2,
mrgLiftM3,
mrgLiftM4,
mrgLiftM5,
mrgAp,
-- ** Strict monadic functions
(.<$!>),
)
where
import Control.Applicative (Alternative)
import Control.Monad (MonadPlus (mplus, mzero), join)
import Grisette.Internal.Core.Control.Monad.Union (MonadUnion)
import Grisette.Internal.Core.Data.Class.LogicalOp (LogicalOp (symNot, (.||)))
import Grisette.Internal.Core.Data.Class.Mergeable
( Mergeable (rootStrategy),
MergingStrategy,
)
import Grisette.Internal.Core.Data.Class.SOrd (SOrd ((.<=)))
import Grisette.Internal.Core.Data.Class.SimpleMergeable (UnionMergeable1, mrgIf)
import Grisette.Internal.Core.Data.Class.TryMerge
( MonadTryMerge,
TryMerge (tryMergeWithStrategy),
tryMerge,
)
import Grisette.Internal.SymPrim.SymBool (SymBool)
import Grisette.Lib.Control.Applicative
( mrgEmpty,
mrgLiftA2,
mrgPure,
(.*>),
(.<$>),
(.<*>),
)
import Grisette.Lib.Data.Foldable
( mrgFoldlM,
mrgForM_,
mrgMapM_,
mrgMsum,
mrgSequenceA_,
mrgSequence_,
)
import Grisette.Lib.Data.Functor (mrgFmap, mrgUnzip, mrgVoid, (.<$))
import Grisette.Lib.Data.Traversable
( mrgForM,
mrgMapM,
mrgSequence,
mrgSequenceA,
mrgTraverse,
)
-- | 'return' with 'MergingStrategy' knowledge propagation.
mrgReturnWithStrategy :: (MonadTryMerge u) => MergingStrategy a -> a -> u a
mrgReturnWithStrategy s = tryMergeWithStrategy s . return
{-# INLINE mrgReturnWithStrategy #-}
-- | '>>=' with 'MergingStrategy' knowledge propagation.
mrgBindWithStrategy ::
(MonadTryMerge u) =>
MergingStrategy a ->
MergingStrategy b ->
u a ->
(a -> u b) ->
u b
mrgBindWithStrategy sa sb a f =
tryMergeWithStrategy sb $ tryMergeWithStrategy sa a >>= f
{-# INLINE mrgBindWithStrategy #-}
-- | 'return' with 'MergingStrategy' knowledge propagation.
mrgReturn :: (MonadTryMerge u, Mergeable a) => a -> u a
mrgReturn = mrgReturnWithStrategy rootStrategy
{-# INLINE mrgReturn #-}
infixl 1 .>>=
-- | '>>=' with 'MergingStrategy' knowledge propagation.
(.>>=) ::
(MonadTryMerge u, Mergeable a, Mergeable b) =>
u a ->
(a -> u b) ->
u b
(.>>=) = mrgBindWithStrategy rootStrategy rootStrategy
{-# INLINE (.>>=) #-}
infixl 1 .>>
-- | '>>' with 'MergingStrategy' knowledge propagation.
--
-- This is usually more efficient than calling the original '>>' and merge the
-- results.
(.>>) :: (MonadTryMerge m, Mergeable a, Mergeable b) => m a -> m b -> m b
a .>> f = tryMerge $ mrgVoid a >> f
{-# INLINE (.>>) #-}
-- | 'fail' with 'MergingStrategy' knowledge propagation.
mrgFail :: (MonadTryMerge m, Mergeable a, MonadFail m) => String -> m a
mrgFail = tryMerge . fail
{-# INLINE mrgFail #-}
-- | 'mzero' with 'MergingStrategy' knowledge propagation.
mrgMzero :: forall m a. (MonadTryMerge m, Mergeable a, MonadPlus m) => m a
mrgMzero = tryMerge mzero
{-# INLINE mrgMzero #-}
-- | 'mplus' with 'MergingStrategy' knowledge propagation.
mrgMplus ::
forall m a. (MonadTryMerge m, Mergeable a, MonadPlus m) => m a -> m a -> m a
mrgMplus a b = tryMerge $ mplus (tryMerge a) (tryMerge b)
{-# INLINE mrgMplus #-}
infixr 1 .=<<
-- | '=<<' with 'MergingStrategy' knowledge propagation.
(.=<<) ::
(MonadTryMerge m, Mergeable a, Mergeable b) => (a -> m b) -> m a -> m b
f .=<< a = tryMerge $ f =<< tryMerge a
{-# INLINE (.=<<) #-}
infixr 1 .>=>
-- | '>=>' with 'MergingStrategy' knowledge propagation.
(.>=>) ::
(MonadTryMerge m, Mergeable a, Mergeable b, Mergeable c) =>
(a -> m b) ->
(b -> m c) ->
a ->
m c
f .>=> g = \a -> tryMerge $ tryMerge (f a) >>= g
{-# INLINE (.>=>) #-}
infixr 1 .<=<
-- | '<=<' with 'MergingStrategy' knowledge propagation.
(.<=<) ::
(MonadTryMerge m, Mergeable a, Mergeable b, Mergeable c) =>
(b -> m c) ->
(a -> m b) ->
a ->
m c
(.<=<) = flip (.>=>)
{-# INLINE (.<=<) #-}
-- | 'forever' with 'MergingStrategy' knowledge propagation.
mrgForever ::
(Applicative m, TryMerge m, Mergeable b, Mergeable a) => m a -> m b
mrgForever a = let a' = a .*> a' in a'
{-# INLINE mrgForever #-}
-- | 'join' with 'MergingStrategy' knowledge propagation.
mrgJoin :: (MonadTryMerge m, Mergeable a) => m (m a) -> m a
mrgJoin a = tryMerge $ join a
{-# INLINE mrgJoin #-}
-- | 'mfilter' with 'MergingStrategy' knowledge propagation.
mrgMfilter ::
(MonadTryMerge m, MonadPlus m, Mergeable a) =>
(a -> Bool) ->
m a ->
m a
mrgMfilter p ma = do
a <- tryMerge ma
if p a then mrgReturn a else mrgMzero
{-# INLINE mrgMfilter #-}
-- | 'mfilter' with 'MergingStrategy' knowledge propagation and symbolic
-- conditions.
symMfilter ::
(MonadTryMerge m, MonadPlus m, MonadUnion m, Mergeable a) =>
(a -> SymBool) ->
m a ->
m a
symMfilter p ma = do
a <- tryMerge ma
mrgIf (p a) (mrgReturn a) mrgMzero
{-# INLINE symMfilter #-}
-- | 'filterM' with 'MergingStrategy' knowledge propagation.
mrgFilterM ::
(TryMerge m, Applicative m, Mergeable a, Foldable t) =>
(a -> m Bool) ->
t a ->
m [a]
mrgFilterM p =
foldr
(\x lst -> (\flg -> if flg then (x :) else id) .<$> p x .<*> lst)
(mrgPure [])
{-# INLINE mrgFilterM #-}
-- | 'filterM' with 'MergingStrategy' knowledge propagation and symbolic
-- conditions.
symFilterM ::
(TryMerge m, MonadUnion m, Mergeable a, Foldable t) =>
(a -> m SymBool) ->
t a ->
m [a]
symFilterM p =
foldr
( \x lst -> do
flag <- tryMerge $ p x
mrgIf flag ((x :) <$> lst) lst
)
(mrgPure [])
{-# INLINE symFilterM #-}
-- | 'mapAndUnzipM' with 'MergingStrategy' knowledge propagation.
mrgMapAndUnzipM ::
( Applicative m,
TryMerge m,
Mergeable b,
Mergeable c
) =>
(a -> m (b, c)) ->
[a] ->
m ([b], [c])
mrgMapAndUnzipM f xs = mrgUnzip .<$> mrgTraverse f xs
{-# INLINE mrgMapAndUnzipM #-}
-- | 'zipWithM' with 'MergingStrategy' knowledge propagation.
mrgZipWithM ::
(Applicative m, TryMerge m, Mergeable c) =>
(a -> b -> m c) ->
[a] ->
[b] ->
m [c]
mrgZipWithM f xs ys = mrgSequenceA (zipWith f xs ys)
{-# INLINE mrgZipWithM #-}
-- | 'zipWithM_' with 'MergingStrategy' knowledge propagation.
mrgZipWithM_ ::
(Applicative m, TryMerge m, Mergeable c) =>
(a -> b -> m c) ->
[a] ->
[b] ->
m ()
mrgZipWithM_ f xs ys = mrgSequenceA_ (zipWith f xs ys)
{-# INLINE mrgZipWithM_ #-}
-- | 'foldM' with 'MergingStrategy' knowledge propagation.
mrgFoldM ::
(MonadTryMerge m, Mergeable b, Foldable t) =>
(b -> a -> m b) ->
b ->
t a ->
m b
mrgFoldM = mrgFoldlM
{-# INLINE mrgFoldM #-}
-- | 'foldM_' with 'MergingStrategy' knowledge propagation.
mrgFoldM_ ::
(MonadTryMerge m, Foldable t, Mergeable b) =>
(b -> a -> m b) ->
b ->
t a ->
m ()
mrgFoldM_ f a xs = mrgFoldlM f a xs .>> mrgPure ()
{-# INLINE mrgFoldM_ #-}
-- | 'replicateM' with 'MergingStrategy' knowledge propagation.
mrgReplicateM ::
(Applicative m, TryMerge m, Mergeable a) =>
Int ->
m a ->
m [a]
mrgReplicateM n = mrgSequenceA . replicate n
{-# INLINE mrgReplicateM #-}
-- | 'replicateM' with 'MergingStrategy' knowledge propagation and symbolic
-- number of elements.
symReplicateM ::
(MonadUnion m, TryMerge m, Mergeable a, Num int, SOrd int) =>
Int ->
int ->
m a ->
m [a]
symReplicateM maxCnt cnt0 f =
loop maxCnt cnt0
where
loop concreteCnt cnt =
mrgIf
(cnt .<= 0 .|| concreteCnt .<= 0)
(mrgPure [])
(mrgLiftA2 (:) f (loop (concreteCnt - 1) (cnt - 1)))
{-# INLINE symReplicateM #-}
-- | 'replicateM_' with 'MergingStrategy' knowledge propagation.
mrgReplicateM_ ::
(Applicative m, TryMerge m, Mergeable a) =>
Int ->
m a ->
m ()
mrgReplicateM_ n = mrgSequenceA_ . replicate n
{-# INLINE mrgReplicateM_ #-}
-- | 'replicateM_' with 'MergingStrategy' knowledge propagation and symbolic
-- number of elements.
symReplicateM_ ::
(MonadUnion m, TryMerge m, Mergeable a, Num int, SOrd int) =>
Int ->
int ->
m a ->
m ()
symReplicateM_ maxCnt cnt0 f =
loop maxCnt cnt0
where
loop concreteCnt cnt =
mrgIf
(cnt .<= 0 .|| concreteCnt .<= 0)
(mrgPure ())
(f .*> (loop (concreteCnt - 1) (cnt - 1)))
{-# INLINE symReplicateM_ #-}
-- | 'guard' with 'MergingStrategy' knowledge propagation.
mrgGuard :: (Alternative m, TryMerge m) => Bool -> m ()
mrgGuard True = mrgPure ()
mrgGuard False = mrgEmpty
{-# INLINE mrgGuard #-}
-- | 'guard' with 'MergingStrategy' knowledge propagation and symbolic
-- conditions.
symGuard :: (UnionMergeable1 m, TryMerge m, Alternative m) => SymBool -> m ()
symGuard b = mrgIf b (mrgPure ()) mrgEmpty
{-# INLINE symGuard #-}
-- | 'when' with 'MergingStrategy' knowledge propagation.
mrgWhen :: (Applicative m, TryMerge m) => Bool -> m () -> m ()
mrgWhen True a = tryMerge a
mrgWhen False _ = mrgPure ()
{-# INLINE mrgWhen #-}
-- | 'when' with 'MergingStrategy' knowledge propagation and symbolic
-- conditions.
symWhen ::
(Applicative m, TryMerge m, UnionMergeable1 m) => SymBool -> m () -> m ()
symWhen b a = mrgIf b a (mrgPure ())
{-# INLINE symWhen #-}
-- | 'unless' with 'MergingStrategy' knowledge propagation.
mrgUnless :: (Applicative m, TryMerge m) => Bool -> m () -> m ()
mrgUnless b = mrgWhen (not b)
{-# INLINE mrgUnless #-}
-- | 'unless' with 'MergingStrategy' knowledge propagation and symbolic
-- conditions.
symUnless ::
(Applicative m, TryMerge m, UnionMergeable1 m) => SymBool -> m () -> m ()
symUnless b = symWhen (symNot b)
{-# INLINE symUnless #-}
-- | 'liftM' with 'MergingStrategy' knowledge propagation.
mrgLiftM ::
(MonadTryMerge m, Mergeable a, Mergeable b) => (a -> b) -> m a -> m b
mrgLiftM f a = f .<$> a
{-# INLINE mrgLiftM #-}
-- | 'liftM2' with 'MergingStrategy' knowledge propagation.
mrgLiftM2 ::
(MonadTryMerge m, Mergeable a, Mergeable b, Mergeable c) =>
(a -> b -> c) ->
m a ->
m b ->
m c
mrgLiftM2 f a b = f .<$> a .<*> b
{-# INLINE mrgLiftM2 #-}
-- | 'liftM3' with 'MergingStrategy' knowledge propagation.
mrgLiftM3 ::
(MonadTryMerge m, Mergeable a, Mergeable b, Mergeable c, Mergeable d) =>
(a -> b -> c -> d) ->
m a ->
m b ->
m c ->
m d
mrgLiftM3 f a b c = f .<$> a .<*> b .<*> c
{-# INLINE mrgLiftM3 #-}
-- | 'liftM4' with 'MergingStrategy' knowledge propagation.
mrgLiftM4 ::
( MonadTryMerge m,
Mergeable a,
Mergeable b,
Mergeable c,
Mergeable d,
Mergeable e
) =>
(a -> b -> c -> d -> e) ->
m a ->
m b ->
m c ->
m d ->
m e
mrgLiftM4 f a b c d = f .<$> a .<*> b .<*> c .<*> d
{-# INLINE mrgLiftM4 #-}
-- | 'liftM5' with 'MergingStrategy' knowledge propagation.
mrgLiftM5 ::
( MonadTryMerge m,
Mergeable a,
Mergeable b,
Mergeable c,
Mergeable d,
Mergeable e,
Mergeable f
) =>
(a -> b -> c -> d -> e -> f) ->
m a ->
m b ->
m c ->
m d ->
m e ->
m f
mrgLiftM5 f a b c d e = f .<$> a .<*> b .<*> c .<*> d .<*> e
{-# INLINE mrgLiftM5 #-}
-- | '<*>' with 'MergingStrategy' knowledge propagation.
mrgAp ::
(MonadTryMerge m, Mergeable a, Mergeable b) => m (a -> b) -> m a -> m b
mrgAp = (.<*>)
{-# INLINE mrgAp #-}
infixl 4 .<$!>
-- | '<$!>' with 'MergingStrategy' knowledge propagation. Merging is always
-- strict so we can directly use '.<$>'.
(.<$!>) ::
(MonadTryMerge m, Mergeable a, Mergeable b) => (a -> b) -> m a -> m b
f .<$!> a = f .<$> a
{-# INLINE (.<$!>) #-}