grisette-monad-coroutine-0.1.0.0: src/Grisette/Lib/Control/Monad/Coroutine.hs
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -Wno-orphans #-}
-- |
-- Module : Grisette.Lib.Control.Monad.Coroutine
-- 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.Lib.Control.Monad.Coroutine
( mrgSuspend,
mrgMapMonad,
mrgMapSuspension,
mrgMapFirstSuspension,
mrgRunCoroutine,
mrgBounce,
mrgPogoStick,
mrgPogoStickM,
mrgFoldRun,
MrgPairBinder,
mrgSequentialBinder,
)
where
import Control.Monad.Coroutine hiding (merge)
import Grisette.Core
import Grisette.Lib.Control.Monad
liftCoroEitherMergingStrategy ::
(Mergeable1 s, Mergeable1 m) =>
MergingStrategy x ->
MergingStrategy (Either (s (Coroutine s m x)) x)
liftCoroEitherMergingStrategy ms =
liftRootStrategy2 (liftRootStrategy (liftRootStrategy ms)) ms
coroEitherMergingStrategy ::
(Mergeable1 s, Mergeable1 m, Mergeable x) =>
MergingStrategy (Either (s (Coroutine s m x)) x)
coroEitherMergingStrategy = liftRootStrategy2 rootStrategy1 rootStrategy
instance
(Mergeable1 m, Mergeable a, Mergeable1 sus) =>
Mergeable (Coroutine sus m a)
where
rootStrategy =
wrapStrategy
(liftRootStrategy coroEitherMergingStrategy)
Coroutine
(\(Coroutine v) -> v)
instance (Mergeable1 m, Mergeable1 sus) => Mergeable1 (Coroutine sus m) where
liftRootStrategy m =
wrapStrategy
(liftRootStrategy $ liftCoroEitherMergingStrategy m)
Coroutine
(\(Coroutine v) -> v)
instance
(UnionLike m, Mergeable a, Mergeable1 sus) =>
SimpleMergeable (Coroutine sus m a)
where
mrgIte = mrgIf
instance
(UnionLike m, Mergeable1 sus) =>
SimpleMergeable1 (Coroutine sus m)
where
liftMrgIte m = mrgIfWithStrategy (SimpleStrategy m)
instance
(UnionLike m, Mergeable1 sus) =>
UnionLike (Coroutine sus m)
where
mergeWithStrategy s ((Coroutine v) :: Coroutine sus m a) =
Coroutine $ mergeWithStrategy (liftCoroEitherMergingStrategy s) v
mrgIfWithStrategy s cond (Coroutine t) (Coroutine f) =
Coroutine $ mrgIfWithStrategy (liftCoroEitherMergingStrategy s) cond t f
single x = Coroutine $ single $ Right x
unionIf cond (Coroutine t) (Coroutine f) =
Coroutine $ unionIf cond t f
instance
(ExtractSymbolics (m (Either (sus (Coroutine sus m a)) a))) =>
ExtractSymbolics (Coroutine sus m a)
where
extractSymbolics (Coroutine v) = extractSymbolics v
-- | Symbolic version of 'Control.Monad.Coroutine.suspend',
-- the result would be merged and propagate the mergeable knowledge.
mrgSuspend ::
forall m s x.
(Functor s, MonadUnion m, Mergeable x, Mergeable1 s) =>
s (Coroutine s m x) ->
Coroutine s m x
mrgSuspend s =
Coroutine
$ mergeWithStrategy
coroEitherMergingStrategy
$ return (Left s)
{-# INLINEABLE mrgSuspend #-}
-- | Symbolic version of 'Control.Monad.Coroutine.mapMonad',
-- the result would be merged and propagate the mergeable knowledge.
mrgMapMonad ::
forall s m m' x.
(Functor s, Mergeable1 s, Mergeable x, Monad m, MonadUnion m') =>
(forall y. m y -> m' y) ->
Coroutine s m x ->
Coroutine s m' x
mrgMapMonad f (Coroutine r) =
Coroutine
{ resume =
f r >>= \x ->
mergeWithStrategy
coroEitherMergingStrategy
$ return
$ map' x
}
where
map' :: Either (s (Coroutine s m x)) x -> Either (s (Coroutine s m' x)) x
map' (Right r1) = Right r1
map' (Left s) = Left $ mrgMapMonad f <$> s
{-# INLINEABLE mrgMapMonad #-}
-- | Symbolic version of 'Control.Monad.Coroutine.mapSuspension',
-- the result would be merged and propagate the mergeable knowledge.
mrgMapSuspension ::
forall s m x s'.
(Functor s, MonadUnion m, Mergeable x, Mergeable1 s') =>
(forall y. s y -> s' y) ->
Coroutine s m x ->
Coroutine s' m x
mrgMapSuspension f (Coroutine r) =
Coroutine
{ resume =
r >>= \x ->
mergeWithStrategy coroEitherMergingStrategy $ return $ map' x
}
where
map' :: Either (s (Coroutine s m x)) x -> Either (s' (Coroutine s' m x)) x
map' (Right r1) = Right r1
map' (Left s) = Left $ f $ mrgMapSuspension f <$> s
{-# INLINEABLE mrgMapSuspension #-}
-- | Symbolic version of 'Control.Monad.Coroutine.mapFirstSuspension',
-- the result would be merged and propagate the mergeable knowledge.
mrgMapFirstSuspension ::
forall s m x.
(Functor s, Mergeable1 s, MonadUnion m, Mergeable x) =>
(forall y. s y -> s y) ->
Coroutine s m x ->
Coroutine s m x
mrgMapFirstSuspension f (Coroutine r) =
Coroutine
{ resume =
r >>= \s -> mrgReturnWithStrategy coroEitherMergingStrategy $
case s of
Right x -> Right x
Left x -> Left $ f x
}
instance Mergeable (Naught x) where
rootStrategy = SimpleStrategy mrgIte
instance Mergeable1 Naught where
liftRootStrategy _ = SimpleStrategy mrgIte
instance SimpleMergeable (Naught x) where
mrgIte _ x _ = x
instance SimpleMergeable1 Naught where
liftMrgIte _ _ x _ = x
-- | Symbolic version of 'Control.Monad.Coroutine.mapFirstSuspension',
-- the result would be merged and propagate the mergeable knowledge.
mrgRunCoroutine ::
(MonadUnion m, Mergeable x) =>
Coroutine Naught m x ->
m x
mrgRunCoroutine (Coroutine r) = do
v <- r
case v of
Left _ -> error "Won't happen"
Right x -> mrgReturn x
-- | Symbolic version of 'Control.Monad.Coroutine.bounce',
-- the result would be merged and propagate the mergeable knowledge.
mrgBounce ::
(Functor s, Mergeable1 s, MonadUnion m, Mergeable x) =>
(s (Coroutine s m x) -> Coroutine s m x) ->
Coroutine s m x ->
Coroutine s m x
mrgBounce f (Coroutine r) = Coroutine $ mergeWithStrategy coroEitherMergingStrategy $ do
r1 <- r
case r1 of
Left s -> resume $ f s
Right x -> return $ Right x
-- | Symbolic version of 'Control.Monad.Coroutine.pogoStick',
-- the result would be merged and propagate the mergeable knowledge.
mrgPogoStick ::
(MonadUnion m, Mergeable x) =>
(s (Coroutine s m x) -> Coroutine s m x) ->
Coroutine s m x ->
m x
mrgPogoStick f (Coroutine r) = do
r1 <- r
case r1 of
Left h -> mrgPogoStick f $ f h
Right v -> mrgReturn v
-- | Symbolic version of 'Control.Monad.Coroutine.pogoStickM',
-- the result would be merged and propagate the mergeable knowledge.
mrgPogoStickM ::
(MonadUnion m, Mergeable x) =>
(s (Coroutine s m x) -> m (Coroutine s m x)) ->
Coroutine s m x ->
m x
mrgPogoStickM f (Coroutine r) = do
r1 <- r
case r1 of
Left h -> do
cs <- f h
mrgPogoStickM f cs
Right v -> mrgReturn v
-- | Symbolic version of 'Control.Monad.Coroutine.foldRun',
-- the result would be merged and propagate the mergeable knowledge.
mrgFoldRun ::
(MonadUnion m, Mergeable x, Mergeable a) =>
(a -> s (Coroutine s m x) -> (a, Coroutine s m x)) ->
a ->
Coroutine s m x ->
m (a, x)
mrgFoldRun f a (Coroutine r) = do
r1 <- r
case r1 of
Left s -> case f a s of
(a1, c1) -> mrgFoldRun f a1 c1
Right v -> mrgReturn (a, v)
-- | Type of functions that can bind two monadic values together, used to
-- combine two coroutines' step results. The result type needs to be mergeable.
type MrgPairBinder bool m =
forall x y r. (Mergeable r) => (x -> y -> m r) -> m x -> m y -> m r
-- | Symbolic version of 'Control.Monad.Coroutine.sequentialBinder',
-- the result would be merged and propagate the mergeable knowledge.
mrgSequentialBinder :: (MonadUnion m) => MrgPairBinder bool m
mrgSequentialBinder f ma mb = merge $ do
a <- ma
b <- mb
f a b