{-# 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 import Control.Applicative (liftA2) 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 #-}