spectacle-1.0.0: src/Control/Monad/Levels/Internal.hs
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE UndecidableInstances #-}
-- | The "Levels" search monad.
--
-- === Reference
--
-- 1. Donnacha Oisín Kidney, Nicolas Wu. 2021. Algebras for Weighted Search.
--
-- @since 1.0.0
module Control.Monad.Levels.Internal
( -- * Levels
Levels,
runLevels,
-- * Levels Transformer
LevelsT (LevelsT, runLevelsT),
foldAlt,
liftLevelsT,
wrapLevelsT,
)
where
import Control.Applicative (Alternative (empty, (<|>)), Applicative (liftA2))
import Control.Monad (ap)
import Control.Monad.Except (MonadError (catchError, throwError))
import Control.Monad.IO.Class (MonadIO, liftIO)
import Control.Monad.Reader (MonadReader (local, reader))
import Control.Monad.State (MonadState (state))
import Control.Monad.Trans.Class (MonadTrans, lift)
import Data.Functor.Identity (Identity, runIdentity)
import Data.Kind (Type)
import Control.Hyper (HyperM (HyperM, invokeM))
import Data.Bag (Bag (None))
import qualified Data.Bag as Bag
-- ---------------------------------------------------------------------------------------------------------------------
type Levels = LevelsT Identity
runLevels :: Levels a -> (Bag a -> b -> b) -> b -> b
runLevels (LevelsT k) cons nil = runIdentity (k (fmap . cons) (pure nil))
-- | @since 1.0.0
instance Foldable Levels where
foldMap f m = runLevels m (mappend . foldMap f) mempty
{-# INLINE foldMap #-}
-- | @since 1.0.0
instance Traversable Levels where
traverse f m = runLevels m (liftA2 (<|>) . fmap foldAlt . traverse f) (pure empty)
-- ---------------------------------------------------------------------------------------------------------------------
newtype LevelsT :: (Type -> Type) -> Type -> Type where
LevelsT :: {runLevelsT :: forall x. (Bag a -> m x -> m x) -> m x -> m x} -> LevelsT m a
-- | Constructs a 'LevelsT' with a single level, the monoid provided.
--
-- @since 1.0.0
foldAlt :: Foldable m => m a -> LevelsT f a
foldAlt xs = LevelsT \cons nil -> cons (foldr Bag.cons Bag.empty xs) nil
liftLevelsT :: Monad m => m (LevelsT m a) -> LevelsT m a
liftLevelsT xs = LevelsT (\cons nil -> xs >>= \xs' -> runLevelsT xs' cons nil)
wrapLevelsT :: Monad m => m (LevelsT m a) -> LevelsT m a
wrapLevelsT xs = LevelsT (\cons nil -> cons None (xs >>= \xs' -> runLevelsT xs' cons nil))
-- | @since 1.0.0
instance Functor (LevelsT m) where
fmap f (LevelsT g) = LevelsT \cons nil -> g (cons . fmap f) nil
{-# INLINE fmap #-}
-- | @since 1.0.0
instance Monad m => Applicative (LevelsT m) where
pure x = LevelsT \cons nil -> cons (Bag.singleton x) nil
{-# INLINE pure #-}
-- TODO: Lower the definition of (<*>) from 'ap'.
(<*>) = ap
{-# INLINE (<*>) #-}
-- | @since 1.0.0
instance Monad m => Monad (LevelsT m) where
LevelsT m >>= k = liftLevelsT (m (\x xs -> pure (foldr ((<|>) . k) (wrapLevelsT xs) x)) (pure empty))
{-# INLINE (>>=) #-}
-- | @since 1.0.0
instance Monad m => Alternative (LevelsT m) where
empty = LevelsT \_ nil -> nil
{-# INLINE empty #-}
LevelsT f <|> LevelsT g = LevelsT \cons nil ->
-- NOTE: The instance given here yields O(n) complexity for (<|>) and is outlined in "Algebras for weighted search."
let fcons x xs = pure (\k -> k (HyperM xs) x)
fnil = pure \k -> k (HyperM fnil) None
gcon y yk = pure \xk x -> cons (x <> y) (invokeM xk >>= (yk >>=))
gnil _ None = nil
gnil xk x = cons x (invokeM xk >>= ($ gnil))
in f fcons fnil >>= (g gcon (pure gnil) >>=)
{-# INLINE (<|>) #-}
-- | @since 1.0.0
instance MonadTrans LevelsT where
lift m = LevelsT \cons nil -> m >>= (`cons` nil) . Bag.singleton
{-# INLINE lift #-}
-- | @since 1.0.0
instance MonadState s m => MonadState s (LevelsT m) where
state = lift . state
{-# INLINE state #-}
-- | @since 1.0.0
instance MonadReader r m => MonadReader r (LevelsT m) where
reader = lift . reader
{-# INLINE reader #-}
local f (LevelsT g) = LevelsT \cons nil ->
local f (g cons nil)
{-# INLINE local #-}
-- | @since 1.0.0
instance MonadError e m => MonadError e (LevelsT m) where
throwError = lift . throwError
{-# INLINE throwError #-}
catchError (LevelsT f) g = LevelsT \cons nil ->
catchError (f cons nil) (\e -> runLevelsT (g e) cons nil)
{-# INLINE catchError #-}
-- | @since 1.0.0
instance MonadIO m => MonadIO (LevelsT m) where
liftIO m = LevelsT \cons nil -> liftIO m >>= (`cons` nil) . Bag.singleton
{-# INLINE liftIO #-}