{-# LANGUAGE DeriveFunctor, FlexibleInstances, MultiParamTypeClasses, RankNTypes, TypeOperators, UndecidableInstances #-}
module Control.Carrier.Cut.Church
(
runCut
, runCutA
, runCutM
, CutC(..)
, module Control.Effect.Cut
, module Control.Effect.NonDet
) where
import Control.Algebra
import Control.Applicative (liftA2)
import Control.Effect.Cut
import Control.Effect.NonDet
import qualified Control.Monad.Fail as Fail
import Control.Monad.Fix
import Control.Monad.IO.Class
import Control.Monad.Trans.Class
import Data.Coerce (coerce)
import Data.Functor.Identity
runCut :: (a -> m b -> m b) -> m b -> m b -> CutC m a -> m b
runCut cons nil fail (CutC runCutC) = runCutC cons nil fail
runCutA :: (Alternative f, Applicative m) => CutC m a -> m (f a)
runCutA = runCut (fmap . (<|>) . pure) (pure empty) (pure empty)
runCutM :: (Applicative m, Monoid b) => (a -> b) -> CutC m a -> m b
runCutM leaf = runCut (fmap . mappend . leaf) (pure mempty) (pure mempty)
newtype CutC m a = CutC (forall b . (a -> m b -> m b) -> m b -> m b -> m b)
deriving (Functor)
instance Applicative (CutC m) where
pure a = CutC (\ cons nil _ -> cons a nil)
{-# INLINE pure #-}
CutC f <*> CutC a = CutC $ \ cons nil fail ->
f (\ f' fs -> a (cons . f') fs fail) nil fail
{-# INLINE (<*>) #-}
instance Alternative (CutC m) where
empty = CutC (\ _ nil _ -> nil)
{-# INLINE empty #-}
CutC l <|> CutC r = CutC (\ cons nil fail -> l cons (r cons nil fail) fail)
{-# INLINE (<|>) #-}
instance Monad (CutC m) where
CutC a >>= f = CutC $ \ cons nil fail ->
a (\ a' as -> runCut cons as fail (f a')) nil fail
{-# INLINE (>>=) #-}
instance Fail.MonadFail m => Fail.MonadFail (CutC m) where
fail s = lift (Fail.fail s)
{-# INLINE fail #-}
instance MonadFix m => MonadFix (CutC m) where
mfix f = CutC $ \ cons nil fail -> mfix
(toCut . f . run . fromCut)
>>= run . runCut (fmap . cons) (pure nil) (pure fail) where
toCut = runCut (fmap . (<|>) . pure) (pure empty) (pure cutfail)
fromCut = runCut (<$) (error "mfix CutC: empty") (error "mfix CutC: cutfail")
{-# INLINE mfix #-}
instance MonadIO m => MonadIO (CutC m) where
liftIO io = lift (liftIO io)
{-# INLINE liftIO #-}
instance MonadPlus (CutC m)
instance MonadTrans CutC where
lift m = CutC (\ cons nil _ -> m >>= flip cons nil)
{-# INLINE lift #-}
instance (Algebra sig m, Effect sig) => Algebra (Cut :+: NonDet :+: sig) (CutC m) where
alg (L Cutfail) = CutC $ \ _ _ fail -> fail
alg (L (Call m k)) = CutC $ \ cons nil fail -> runCut (\ a as -> runCut cons as fail (k a)) nil nil m
alg (R (L (L Empty))) = empty
alg (R (L (R (Choose k)))) = k True <|> k False
alg (R (R other)) = CutC $ \ cons nil fail -> alg (thread (pure ()) dst other) >>= runIdentity . runCut (coerce cons) (coerce nil) (coerce fail) where
dst :: Applicative m => CutC Identity (CutC m a) -> m (CutC Identity a)
dst = runIdentity . runCut (fmap . liftA2 (<|>) . runCut (fmap . (<|>) . pure) (pure empty) (pure cutfail)) (pure (pure empty)) (pure (pure cutfail))
{-# INLINE alg #-}