fused-effects-1.1.2.7: src/Control/Carrier/Choose/Church.hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
{- | A carrier for 'Choose' effects (nondeterminism without failure).
Under the hood, it uses a Church-encoded binary tree to avoid the problems associated with a naïve list-based implementation (see ["ListT done right"](http://wiki.haskell.org/ListT_done_right)).
@since 1.0.0.0
-}
module Control.Carrier.Choose.Church
( -- * Choose carrier
runChoose
, runChooseS
, ChooseC(..)
-- * Choose effect
, module Control.Effect.Choose
) where
import Control.Algebra
#if !MIN_VERSION_base(4,18,0)
import Control.Applicative (liftA2)
#endif
import Control.Effect.Choose
import 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
import Data.List.NonEmpty (NonEmpty(..), head, tail)
import Data.Semigroup as S
import Prelude hiding (head, tail)
-- | Run a 'Choose' effect with continuations respectively interpreting '<|>' and 'pure'.
--
-- @
-- runChoose fork leaf ('pure' a '<|>' b) = leaf a \`fork\` 'runChoose' fork leaf b
-- @
--
-- @since 1.0.0.0
runChoose :: (m b -> m b -> m b) -> (a -> m b) -> ChooseC m a -> m b
runChoose fork leaf (ChooseC runChooseC) = runChooseC fork leaf
{-# INLINE runChoose #-}
-- | Run a 'Choose' effect, mapping results into a 'S.Semigroup'.
--
-- @since 1.0.0.0
runChooseS :: (S.Semigroup b, Applicative m) => (a -> m b) -> ChooseC m a -> m b
runChooseS = runChoose (liftA2 (S.<>))
{-# INLINE runChooseS #-}
-- | A carrier for 'Choose' effects based on Ralf Hinze’s design described in [Deriving Backtracking Monad Transformers](https://www.cs.ox.ac.uk/ralf.hinze/publications/#P12).
--
-- @since 1.0.0.0
newtype ChooseC m a = ChooseC (forall b . (m b -> m b -> m b) -> (a -> m b) -> m b)
deriving (Functor)
instance Applicative (ChooseC m) where
pure a = ChooseC (\ _ leaf -> leaf a)
{-# INLINE pure #-}
ChooseC f <*> ChooseC a = ChooseC $ \ fork leaf ->
f fork (\ f' -> a fork (leaf . f'))
{-# INLINE (<*>) #-}
instance Monad (ChooseC m) where
ChooseC a >>= f = ChooseC $ \ fork leaf ->
a fork (runChoose fork leaf . f)
{-# INLINE (>>=) #-}
instance Fail.MonadFail m => Fail.MonadFail (ChooseC m) where
fail s = lift (Fail.fail s)
{-# INLINE fail #-}
-- | Separate fixpoints are computed for each branch.
instance MonadFix m => MonadFix (ChooseC m) where
mfix f = ChooseC $ \ fork leaf ->
mfix (runChooseS (pure . pure) . f . head)
>>= \case
a :| [] -> leaf a
a :| _ -> leaf a `fork` runChoose fork leaf (mfix (liftAll . fmap tail . runChooseS (pure . pure) . f))
where
liftAll m = ChooseC $ \ fork leaf -> m >>= foldr1 fork . fmap leaf
{-# INLINE mfix #-}
instance MonadIO m => MonadIO (ChooseC m) where
liftIO io = lift (liftIO io)
{-# INLINE liftIO #-}
instance MonadTrans ChooseC where
lift m = ChooseC (\ _ leaf -> m >>= leaf)
{-# INLINE lift #-}
instance Algebra sig m => Algebra (Choose :+: sig) (ChooseC m) where
alg hdl sig ctx = ChooseC $ \ fork leaf -> case sig of
L Choose -> leaf (True <$ ctx) `fork` leaf (False <$ ctx)
R other -> thread (dst ~<~ hdl) other (pure ctx) >>= run . runChoose (coerce fork) (coerce leaf)
where
dst :: Applicative m => ChooseC Identity (ChooseC m a) -> m (ChooseC Identity a)
dst = run . runChoose (liftA2 (liftA2 (<|>))) (pure . runChoose (liftA2 (<|>)) (pure . pure))
{-# INLINE alg #-}