objective-0.6.5.1: src/Control/Object/Mortal.hs
{-# LANGUAGE Trustworthy #-}
{-# LANGUAGE Rank2Types #-}
module Control.Object.Mortal (
Mortal(..),
mortal,
mortal_,
runMortal,
runMortal',
immortal,
reincarnation
) where
import Control.Object.Object
import Control.Applicative
import Control.Monad.Trans.Either
import Control.Monad
import Control.Monad.Trans.Class
import Unsafe.Coerce
-- | Object with a final result.
--
-- @Object f g ≡ Mortal f g Void@
--
newtype Mortal f g a = Mortal { unMortal :: Object f (EitherT a g) }
instance (Functor m, Monad m) => Functor (Mortal f m) where
fmap f (Mortal obj) = Mortal (obj @>>^ bimapEitherT f id)
{-# INLINE fmap #-}
instance (Functor m, Monad m) => Applicative (Mortal f m) where
pure = return
{-# INLINE pure #-}
(<*>) = ap
{-# INLINE (<*>) #-}
instance Monad m => Monad (Mortal f m) where
return a = mortal $ const $ left a
{-# INLINE return #-}
m >>= k = mortal $ \f -> lift (runMortal' m f) >>= \r -> case r of
Left a -> runMortal (k a) f
Right (x, m') -> return (x, m' >>= k)
instance MonadTrans (Mortal f) where
lift m = mortal $ const $ EitherT $ liftM Left m
mortal :: (forall x. f x -> EitherT a m (x, Mortal f m a)) -> Mortal f m a
mortal f = Mortal (Object (fmap unsafeCoerce f))
{-# INLINE mortal #-}
runMortal :: Mortal f m a -> f x -> EitherT a m (x, Mortal f m a)
runMortal = unsafeCoerce
{-# INLINE runMortal #-}
runMortal' :: Mortal f m a -> f x -> m (Either a (x, Mortal f m a))
runMortal' = unsafeCoerce
{-# INLINE runMortal' #-}
-- | Restricted 'Mortal' constuctor, which can be applied to 'transit', 'fromFoldable' without ambiguousness.
mortal_ :: Object f (EitherT () g) -> Mortal f g ()
mortal_ = Mortal
{-# INLINE mortal_ #-}
immortal :: Monad m => Object f m -> Mortal f m x
immortal obj = mortal $ \f -> EitherT $ runObject obj f >>= \(a, obj') -> return $ Right (a, immortal obj')
reincarnation :: Monad m => (a -> Mortal f m a) -> a -> Object f m
reincarnation g = go . g where
go m = Object $ \f -> runMortal' m f >>= \r -> case r of
Left a -> runObject (go (g a)) f
Right (a, m') -> return (a, go m')
{-# INLINE reincarnation #-}