packages feed

in-other-words-0.2.0.0: src/Control/Monad/Trans/Free/Church/Alternate.hs

module Control.Monad.Trans.Free.Church.Alternate where

import Control.Applicative
import Control.Monad
import Control.Monad.Trans
import Control.Monad.Base
import qualified Control.Monad.Fail as Fail
import Control.Effect.Internal.Union
import Control.Effect.Internal.Utils
import Control.Effect.Type.Unravel
import Control.Effect.Type.ListenPrim
import Control.Effect.Type.ReaderPrim
import Control.Effect.Type.Regional
import Control.Effect.Type.Optional
import Control.Monad.Catch hiding (handle)

newtype FreeT f (m :: * -> *) a = FreeT {
    unFreeT :: forall r
             . (forall x. f x -> (x -> m r) -> m r)
            -> (a -> m r) -> m r
  }

data Coyoneda f a where
  Coyoneda :: (x -> a) -> f x -> Coyoneda f a

newtype LayeredFreeT f m a = LayeredFreeT {
    unLayeredFreeT :: m (Either a (Coyoneda f (LayeredFreeT f m a)))
  }

toLayeredFreeT :: Monad m => FreeT f m a -> LayeredFreeT f m a
toLayeredFreeT free =
  LayeredFreeT $ unFreeT free
                         (\fx c -> return $ Right $ Coyoneda (LayeredFreeT #. c) fx)
                         (return . Left)

fromLayeredFreeT :: Monad m => LayeredFreeT f m a -> FreeT f m a
fromLayeredFreeT stack0 = FreeT $ \h c ->
  let
    go stack = unLayeredFreeT stack >>= \case
      Left a -> c a
      Right (Coyoneda cn f) -> h f (go . cn)
  in
    go stack0

class    (forall f. Threads (FreeT f) p) => FreeThreads p
instance (forall f. Threads (FreeT f) p) => FreeThreads p

liftF :: f a -> FreeT f m a
liftF f = FreeT $ \h c -> f `h` c
{-# INLINE liftF #-}

foldFreeT :: Monad m
          => (a -> b)
          -> (forall x. (x -> m b) -> f x -> m b)
          -> FreeT f m a
          -> m b
foldFreeT b c free = unFreeT free (flip c) (return . b)
{-# INLINE foldFreeT #-}

foldFreeT' :: Monad m
           => (m r -> r)
           -> (a -> r)
           -> (forall x. (x -> r) -> f x -> r)
           -> FreeT f m a
           -> r
foldFreeT' bind b h free =
  bind $ unFreeT free (\fx c -> return $ h (bind . c) fx) (return . b)
{-# INLINE foldFreeT' #-}

instance Functor (FreeT f m) where
  fmap f cnt = FreeT $ \handler c ->
    unFreeT cnt handler (c . f)
  {-# INLINE fmap #-}

instance Applicative (FreeT f m) where
  pure a = FreeT $ \_ c -> c a
  {-# INLINE pure #-}

  ff <*> fa = FreeT $ \handler c ->
    unFreeT ff handler $ \f ->
    unFreeT fa handler (c . f)
  {-# INLINE (<*>) #-}

  liftA2 f fa fb = FreeT $ \handler c ->
    unFreeT fa handler $ \a ->
    unFreeT fb handler (c . f a)
  {-# INLINE liftA2 #-}

  fa *> fb = fa >>= \_ -> fb
  {-# INLINE (*>) #-}

instance Monad (FreeT f m) where
  m >>= f = FreeT $ \handler c ->
    unFreeT m handler $ \a ->
    unFreeT (f a) handler c
  {-# INLINE (>>=) #-}

instance MonadBase b m => MonadBase b (FreeT f m) where
  liftBase = lift . liftBase
  {-# INLINE liftBase #-}

instance Fail.MonadFail m => Fail.MonadFail (FreeT f m) where
  fail = lift . Fail.fail
  {-# INLINE fail #-}

instance MonadTrans (FreeT f) where
  lift m = FreeT $ \_ c -> m >>= c
  {-# INLINE lift #-}

instance MonadIO m => MonadIO (FreeT f m) where
  liftIO = lift . liftIO
  {-# INLINE liftIO #-}

instance MonadThrow m => MonadThrow (FreeT f m) where
  throwM = lift . throwM
  {-# INLINE throwM #-}

instance MonadCatch m => MonadCatch (FreeT f m) where
  catch main handle = FreeT $ \handler c ->
    foldFreeT' (join . (`catch` \e -> return $ unFreeT (handle e) handler c))
               c
               (flip handler)
               main
  {-# INLINE catch #-}

instance Monoid w => ThreadsEff (FreeT f) (ListenPrim w) where
  threadEff = threadListenPrim $ \alg main -> FreeT $ \h c ->
    foldFreeT' (\m acc -> alg (ListenPrimListen m) >>= \(s', f) -> f $! acc <> s'
               )
               (\a acc -> c (acc, a))
               (\cn fx acc -> h fx (`cn` acc))
               main
               mempty

instance ThreadsEff (FreeT f) (Regional s) where
  threadEff = threadRegionalViaOptional
  {-# INLINE threadEff #-}

instance Functor s => ThreadsEff (FreeT f) (Optional s) where
  threadEff alg (Optionally sa main) = FreeT $ \h c ->
    foldFreeT' (\m -> join $ alg $ Optionally (fmap c sa) m)
               c
               (flip h)
               main
  {-# INLINE threadEff #-}

instance ThreadsEff (FreeT f) (Unravel p) where
  threadEff alg (Unravel p cataM main) =
    let
      n = \m' -> alg (Unravel p (cataM . lift) m')
    in
      lift $ n
           $ unFreeT main
                     (\fx cn -> return $ cataM $ liftF fx >>= lift . n . cn)
                     return
  {-# INLINE threadEff #-}

instance ThreadsEff (FreeT f) (ReaderPrim i) where
  threadEff = threadReaderPrimViaRegional
  {-# INLINE threadEff #-}