logict-sequence-0.2.0.2: src/Control/Monad/Logic/Sequence/Internal.hs
{-# LANGUAGE CPP #-}
#include "logict-sequence.h"
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveTraversable #-}
#if __GLASGOW_HASKELL__ < 710
{-# LANGUAGE DeriveFoldable #-}
{-# LANGUAGE DeriveFunctor #-}
#endif
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
#ifdef USE_PATTERN_SYNONYMS
{-# LANGUAGE PatternSynonyms #-}
#endif
{-# LANGUAGE Trustworthy #-}
{-# OPTIONS_HADDOCK not-home #-}
#if __GLASGOW_HASKELL__ >= 902
-- We need this for now to work around
-- https://gitlab.haskell.org/ghc/ghc/-/issues/22549 which otherwise causes
-- infinite loops in several instances. It's definitely needed for GHC 9.4; we
-- do it for 9.2 as well just in case, as others have gotten loops with
-- -fdicts-strict with that version.
{-# OPTIONS_GHC -fno-dicts-strict #-}
#endif
{- OPTIONS_GHC -ddump-simpl -dsuppress-coercions #-}
-- | Based on the LogicT improvements in the paper, Reflection without
-- Remorse. Code is based on the code provided in:
-- https://github.com/atzeus/reflectionwithoutremorse
--
-- Note: that code is provided under an MIT license, so we use that as
-- well.
module Control.Monad.Logic.Sequence.Internal
(
#ifdef USE_PATTERN_SYNONYMS
SeqT(MkSeqT, getSeqT, ..)
#else
SeqT(..)
#endif
, Seq
#ifdef USE_PATTERN_SYNONYMS
, pattern MkSeq
, getSeq
#endif
, ViewT(..)
, View
, viewT
, view
, toViewT
, toView
, fromViewT
, fromView
, observeAllT
, observeAll
, observeManyT
, observeMany
, observeT
, observe
, fromSeqT
, hoistPre
, hoistPost
, hoistPreUnexposed
, hoistPostUnexposed
, toLogicT
, fromLogicT
, cons
, consM
, choose
, chooseM
)
where
import Control.Applicative
import Control.Monad hiding (liftM)
#if !MIN_VERSION_base(4,8,0)
import qualified Control.Monad as Monad
#endif
import qualified Control.Monad.Fail as Fail
import Control.Monad.Identity (Identity(..))
import Control.Monad.Trans (MonadTrans(..))
import Control.Monad.Logic.Class
import qualified Control.Monad.Logic as L
import Control.Monad.IO.Class
import Control.Monad.Reader.Class (MonadReader (..))
import Control.Monad.State.Class (MonadState (..))
import Control.Monad.Error.Class (MonadError (..))
import Control.Monad.Morph (MFunctor (..))
import qualified Data.SequenceClass as S
import Control.Monad.Logic.Sequence.Internal.Queue (Queue)
#if MIN_VERSION_base(4,8,0)
import Control.Monad.Zip (MonadZip (..))
#endif
import qualified Text.Read as TR
import Data.Function (on)
#if MIN_VERSION_base(4,9,0)
import Data.Functor.Classes
#endif
#if !MIN_VERSION_base(4,8,0)
import Data.Monoid (Monoid(..))
#endif
#if MIN_VERSION_base(4,9,0)
import Data.Semigroup (Semigroup(..))
#endif
import qualified Data.Foldable as F
import qualified Data.Traversable as T
import GHC.Generics (Generic)
import Data.Coerce (coerce)
-- | A view of the front end of a 'SeqT'.
data ViewT m a = Empty | a :< SeqT m a
deriving Generic
infixl 5 :<
type View = ViewT Identity
-- | A catamorphism for 'ViewT's
viewT :: b -> (a -> SeqT m a -> b) -> ViewT m a -> b
viewT n _ Empty = n
viewT _ c (a :< s) = c a s
{-# INLINE viewT #-}
-- | A catamorphism for 'View's. Note that this is just a type-restricted version
-- of 'viewT'.
view :: b -> (a -> Seq a -> b) -> View a -> b
view = viewT
{-# INLINE view #-}
deriving instance (Show a, Show (SeqT m a)) => Show (ViewT m a)
deriving instance (Read a, Read (SeqT m a)) => Read (ViewT m a)
deriving instance (Eq a, Eq (SeqT m a)) => Eq (ViewT m a)
deriving instance (Ord a, Ord (SeqT m a)) => Ord (ViewT m a)
deriving instance Monad m => Functor (ViewT m)
deriving instance (Monad m, F.Foldable m) => F.Foldable (ViewT m)
instance (Monad m, T.Traversable m) => T.Traversable (ViewT m) where
traverse _ Empty = pure Empty
traverse f (x :< xs) =
liftA2 (\y ys -> y :< fromViewT ys) (f x) (T.traverse (T.traverse f) . toViewT $ xs)
-- The derived instance would use
--
-- traverse f (x :< xs) = liftA2 (:<) (f x) (traverse f xs)
--
-- Inlining the inner `traverse` reveals an application of `fmap` which
-- we fuse with `liftA2`, in case `fmap` isn't free.
#if MIN_VERSION_base(4,9,0)
instance (Eq1 m, Monad m) => Eq1 (ViewT m) where
liftEq _ Empty Empty = True
liftEq eq (a :< s) (b :< t) = eq a b && liftEq eq s t
liftEq _ _ _ = False
instance (Ord1 m, Monad m) => Ord1 (ViewT m) where
liftCompare _ Empty Empty = EQ
liftCompare _ Empty (_ :< _) = LT
liftCompare cmp (a :< s) (b :< t) = cmp a b `mappend` liftCompare cmp s t
liftCompare _ (_ :< _) Empty = GT
instance (Show1 m, Monad m) => Show1 (ViewT m) where
liftShowsPrec sp sl d val = case val of
Empty -> ("Empty" ++)
a :< s -> showParen (d > 5) $
sp 6 a .
showString " :< " .
liftShowsPrec sp sl 6 s
#endif
-- | An asymptotically efficient logic monad transformer. It is generally best to
-- think of this as being defined
--
-- @
-- newtype SeqT m a = 'MkSeqT' { 'getSeqT' :: m ('ViewT' m a) }
-- @
--
-- Using the 'MkSeqT' pattern synonym with 'getSeqT', you can (almost) pretend
-- it's really defined this way! However, the real implementation is different,
-- so as to be more efficient in the face of deeply left-associated `<|>` or
-- `mplus` applications.
newtype SeqT m a = SeqT (Queue (m (ViewT m a)))
#ifdef USE_PATTERN_SYNONYMS
pattern MkSeqT :: Monad m => m (ViewT m a) -> SeqT m a
pattern MkSeqT{getSeqT} <- (toViewT -> getSeqT)
where
MkSeqT = fromViewT
{-# COMPLETE MkSeqT #-}
pattern MkSeq :: View a -> Seq a
pattern MkSeq{getSeq} = MkSeqT (Identity getSeq)
{-# COMPLETE MkSeq #-}
#endif
-- | A specialization of 'SeqT' to the 'Identity' monad. You can
-- imagine that this is defined
--
-- @
-- newtype Seq a = MkSeq { getSeq :: ViewT Identity a }
-- @
--
-- Using the 'MkSeq' pattern synonym with 'getSeq', you can pretend it's
-- really defined this way! However, the real implementation is different,
-- so as to be more efficient in the face of deeply left-associated `<|>`
-- or `mplus` applications.
type Seq = SeqT Identity
fromViewT :: m (ViewT m a) -> SeqT m a
fromViewT = SeqT . S.singleton
{-# INLINABLE [1] fromViewT #-}
fromView :: forall a. View a -> Seq a
fromView = coerce (fromViewT :: Identity (View a) -> Seq a)
{-# INLINE fromView #-}
toViewT :: Monad m => SeqT m a -> m (ViewT m a)
toViewT (SeqT s) = case S.viewl s of
S.EmptyL -> return Empty
h S.:< t -> h >>= \x -> case x of
Empty -> toViewT (SeqT t)
hi :< SeqT ti -> return (hi :< SeqT (ti S.>< t))
{-# INLINEABLE [1] toViewT #-}
toView :: forall a. Seq a -> View a
toView = coerce (toViewT :: SeqT Identity a -> Identity (ViewT Identity a))
{-# INLINABLE toView #-}
-- For now, we don't assume the monad identity law holds for the underlying
-- monad. We may re-evaluate that later, but it's a bit tricky to document the
-- detailed strictness requirements properly.
--
-- We do, however, assume that `pure /= _|_`, or that `>>=` doesn't `seq` on
-- its second argument, and that we can therefore eta-reduce `\x -> pure x` to
-- just `pure`. It seems quite safe to assume that at least one of these is
-- true, since in real code they're virtually always *both* true.
{-# RULES
"toViewT . fromViewT" forall m. toViewT (fromViewT m) = m >>= return
#-}
{-
Theorem: toViewT . fromViewT = id
Proof:
toViewT (fromViewT m)
=
toViewT (SeqT (singleton m))
=
case viewl (singleton m) of
h S.:< t -> h >>= \x -> case x of
Empty -> toViewT (SeqT t)
hi :< SeqT ti -> return (hi :< SeqT (ti S.>< t))
=
m >>= \x -> case x of
Empty -> toViewT (SeqT S.empty)
hi :< SeqT ti -> return (hi :< SeqT ti)
=
m >>= \x -> case x of
Empty -> return Empty
hi :< SeqT ti -> return (hi :< SeqT ti)
= m >>= \x -> return x
= m -- assuming the appropriate identity law holds for the underlying monad.
-}
instance (Show (m (ViewT m a)), Monad m) => Show (SeqT m a) where
showsPrec d s = showParen (d > app_prec) $
showString "MkSeqT " . showsPrec (app_prec + 1) (toViewT s)
where app_prec = 10
instance Read (m (ViewT m a)) => Read (SeqT m a) where
readPrec = TR.parens $ TR.prec app_prec $ do
TR.Ident "MkSeqT" <- TR.lexP
m <- TR.step TR.readPrec
return (fromViewT m)
where app_prec = 10
readListPrec = TR.readListPrecDefault
instance (Eq a, Eq (m (ViewT m a)), Monad m) => Eq (SeqT m a) where
(==) = (==) `on` toViewT
instance (Ord a, Ord (m (ViewT m a)), Monad m) => Ord (SeqT m a) where
compare = compare `on` toViewT
#if MIN_VERSION_base(4,9,0)
instance (Eq1 m, Monad m) => Eq1 (SeqT m) where
liftEq eq s t = liftEq (liftEq eq) (toViewT s) (toViewT t)
instance (Ord1 m, Monad m) => Ord1 (SeqT m) where
liftCompare eq s t = liftCompare (liftCompare eq) (toViewT s) (toViewT t)
instance (Show1 m, Monad m) => Show1 (SeqT m) where
liftShowsPrec sp sl d s = showParen (d > app_prec) $
showString "MkSeqT " . liftShowsPrec (liftShowsPrec sp sl) (liftShowList sp sl) (app_prec + 1) (toViewT s)
where app_prec = 10
#endif
single :: Monad m => a -> m (ViewT m a)
single a = return (a :< mzero)
{-# INLINE single #-}
instance Monad m => Functor (SeqT m) where
{-# INLINEABLE fmap #-}
fmap f (SeqT q) = SeqT $ fmap (myliftM (fmap f)) q
{-# INLINABLE (<$) #-}
x <$ SeqT q = SeqT $ fmap (myliftM (x <$)) q
instance Monad m => Applicative (SeqT m) where
{-# INLINE pure #-}
{-# INLINABLE (<*>) #-}
pure = fromViewT . single
#if MIN_VERSION_base(4,8,0)
fs <*> xs = fs >>= \f -> f <$> xs
#else
(<*>) = ap
#endif
{-# INLINABLE (*>) #-}
(toViewT -> m) *> n = fromViewT $ thenViewT m n
#if MIN_VERSION_base(4,10,0)
liftA2 f xs ys = xs >>= \x -> f x <$> ys
{-# INLINABLE liftA2 #-}
#endif
thenViewT :: Monad m => m (ViewT m a) -> SeqT m b -> m (ViewT m b)
thenViewT m n = m >>= \x -> case x of
Empty -> return Empty
_ :< t -> toViewT n `altViewT` (t *> n)
{-# INLINABLE thenViewT #-}
instance Monad m => Alternative (SeqT m) where
{-# INLINE empty #-}
{-# INLINEABLE (<|>) #-}
empty = SeqT S.empty
SeqT m <|> SeqT n = SeqT (m S.>< n)
-- |
-- @
-- altViewT s t = toViewT (fromViewT s <|> t)
-- @
--
-- Question: is this actually good for optimization?
altViewT :: Monad m => m (ViewT m a) -> SeqT m a -> m (ViewT m a)
altViewT m n = m >>= \x -> case x of
Empty -> toViewT n
h :< t -> return (h :< (t <|> n))
{-# INLINABLE altViewT #-}
-- | @cons a s = pure a <|> s@
cons :: Monad m => a -> SeqT m a -> SeqT m a
cons a s = fromViewT (return (a :< s))
{-# INLINE cons #-}
-- | @consM m s = lift m <|> s@
consM :: Monad m => m a -> SeqT m a -> SeqT m a
consM m s = fromViewT (myliftM (:< s) m)
{-# INLINE consM #-}
instance Monad m => Monad (SeqT m) where
{-# INLINE return #-}
{-# INLINABLE (>>=) #-}
return = pure
(toViewT -> m) >>= f = fromViewT $ bindViewT m f
(>>) = (*>)
#if !MIN_VERSION_base(4,13,0)
{-# INLINEABLE fail #-}
fail = Fail.fail
#endif
bindViewT :: Monad m => m (ViewT m a) -> (a -> SeqT m b) -> m (ViewT m b)
bindViewT m f = m >>= \x -> case x of
Empty -> return Empty
h :< t -> toViewT (f h) `altViewT` (t >>= f)
{-# INLINABLE bindViewT #-}
instance Monad m => Fail.MonadFail (SeqT m) where
{-# INLINEABLE fail #-}
fail _ = SeqT S.empty
instance Monad m => MonadPlus (SeqT m) where
{-# INLINE mzero #-}
{-# INLINE mplus #-}
mzero = Control.Applicative.empty
mplus = (<|>)
#if MIN_VERSION_base(4,9,0)
instance Monad m => Semigroup (SeqT m a) where
{-# INLINE (<>) #-}
{-# INLINE sconcat #-}
(<>) = mplus
sconcat = F.asum
#endif
instance Monad m => Monoid (SeqT m a) where
{-# INLINE mempty #-}
{-# INLINE mconcat #-}
mempty = SeqT S.empty
mconcat = F.asum
#if !MIN_VERSION_base(4,11,0)
{-# INLINE mappend #-}
mappend = (<|>)
#endif
instance MonadTrans SeqT where
{-# INLINE lift #-}
lift m = fromViewT (m >>= single)
instance Monad m => MonadLogic (SeqT m) where
{-# INLINE msplit #-}
msplit (toViewT -> m) = fromViewT $ do
r <- m
case r of
Empty -> single Nothing
a :< t -> single (Just (a, t))
interleave m1 m2 = fromViewT $ interleaveViewT m1 m2
(toViewT -> m) >>- f = fromViewT $ m >>= viewT
(return Empty) (\a m' -> interleaveViewT (f a) (m' >>- f))
ifte (toViewT -> t) th (toViewT -> el) = fromViewT $ t >>= viewT
el
(\a s -> altViewT (toViewT (th a)) (s >>= th))
once (toViewT -> m) = fromViewT $ m >>= viewT
(return Empty)
(\a _ -> single a)
lnot (toViewT -> m) = fromViewT $ m >>= viewT
(single ()) (\ _ _ -> return Empty)
-- | A version of 'interleave' that produces a view instead of a
-- 'SeqT'. This lets us avoid @toViewT . fromViewT@ in '>>-'.
interleaveViewT :: Monad m => SeqT m a -> SeqT m a -> m (ViewT m a)
interleaveViewT (toViewT -> m1) m2 = m1 >>= viewT
(toViewT m2)
(\a m1' -> return $ a :< interleave m2 m1')
-- | @choose = foldr (\a s -> pure a <|> s) empty@
--
-- @choose :: Monad m => [a] -> SeqT m a@
choose :: (F.Foldable t, Monad m) => t a -> SeqT m a
choose = F.foldr cons empty
{-# INLINABLE choose #-}
-- | @chooseM = foldr (\ma s -> lift ma <|> s) empty@
--
-- @chooseM :: Monad m => [m a] -> SeqT m a@
chooseM :: (F.Foldable t, Monad m) => t (m a) -> SeqT m a
-- The idea here, which I hope is sensible, is to avoid building and
-- restructuring queues unnecessarily. We end up building only *singleton*
-- queues, which should hopefully be pretty cheap.
chooseM = F.foldr consM empty
{-# INLINABLE chooseM #-}
-- | Perform all the actions in a 'SeqT' and gather the results.
observeAllT :: Monad m => SeqT m a -> m [a]
observeAllT (toViewT -> m) = m >>= go where
go (a :< t) = myliftM (a:) (toViewT t >>= go)
go _ = return []
{-# INLINEABLE observeAllT #-}
-- | Perform actions in a 'SeqT' until one of them produces a
-- result. Returns 'Nothing' if there are no results.
observeT :: Monad m => SeqT m a -> m (Maybe a)
observeT (toViewT -> m) = m >>= go where
go (a :< _) = return (Just a)
go Empty = return Nothing
{-# INLINE observeT #-}
-- | @observeManyT n s@ performs actions in @s@ until it produces
-- @n@ results or terminates. All the gathered results are returned.
observeManyT :: Monad m => Int -> SeqT m a -> m [a]
observeManyT k m = toViewT m >>= go k where
go n _ | n <= 0 = return []
go _ Empty = return []
go n (a :< t) = myliftM (a:) (observeManyT (n-1) t)
{-# INLINEABLE observeManyT #-}
-- | Get the first result in a 'Seq', if there is one.
observe :: Seq a -> Maybe a
observe = runIdentity . observeT
{-# INLINE observe #-}
-- | Get all the results in a 'Seq'.
observeAll :: Seq a -> [a]
observeAll = runIdentity . observeAllT
{-# INLINE observeAll #-}
-- | @observeMany n s@ gets up to @n@ results from a 'Seq'.
observeMany :: Int -> Seq a -> [a]
observeMany n = runIdentity . observeManyT n
{-# INLINE observeMany #-}
-- | Convert @'SeqT' m a@ to @t m a@ when @t@ is some other logic monad
-- transformer.
fromSeqT :: (Monad m, Monad (t m), MonadTrans t, Alternative (t m)) => SeqT m a -> t m a
fromSeqT (toViewT -> m) = lift m >>= \r -> case r of
Empty -> empty
a :< s -> pure a <|> fromSeqT s
-- | Convert @'SeqT' m a@ to @'L.LogicT' m a@.
--
-- @ toLogicT = 'fromSeqT' @
toLogicT :: Monad m => SeqT m a -> L.LogicT m a
toLogicT = fromSeqT
fromLogicT :: Monad m => L.LogicT m a -> SeqT m a
fromLogicT (L.LogicT f) = fromViewT $ f (\a v -> return (a :< fromViewT v)) (return Empty)
instance (Monad m, F.Foldable m) => F.Foldable (SeqT m) where
foldMap f = F.foldMap (F.foldMap f) . toViewT
instance (Monad m, T.Traversable m) => T.Traversable (SeqT m) where
-- Why is this lawful? It comes down to the fact that toViewT and
-- fromViewT are inverses, modulo representation and detailed
-- strictness. They witness a sort of stepwise isomorphism between
-- SeqT and the obviously traversable
--
-- newtype ML m a = ML (m (ViewT m a))
--
-- Why can't we just use the derived Traversable instance? It doesn't
-- respect ==. See https://github.com/dagit/logict-sequence/issues/51#issuecomment-896242724
-- for an example.
traverse f = fmap fromViewT . T.traverse (T.traverse f) . toViewT
-- | 'hoist' is 'hoistPre'.
instance MFunctor SeqT where
-- Note: if `f` is not a monad morphism, then hoist may not respect
-- (==). That is, it could be that
--
-- s == t = True
--
-- but
--
-- hoist f s == hoist f t = False..
--
-- This behavior is permitted by the MFunctor
-- documentation, and allows us to avoid restructuring
-- the SeqT.
hoist f = hoistPre f
-- | This function is the implementation of 'hoist' for 'SeqT'. The passed
-- function is required to be a monad morphism.
hoistPre :: Monad m => (forall x. m x -> n x) -> SeqT m a -> SeqT n a
hoistPre f (SeqT s) = SeqT $ fmap (f . myliftM go) s
where
go Empty = Empty
go (a :< as) = a :< hoistPre f as
-- | A version of `hoist` that uses the `Monad` instance for @n@
-- rather than for @m@. Like @hoist@, the passed function is required
-- to be a monad morphism.
hoistPost :: Monad n => (forall x. m x -> n x) -> SeqT m a -> SeqT n a
hoistPost f (SeqT s) = SeqT $ fmap (myliftM go . f) s
where
go Empty = Empty
go (a :< as) = a :< hoistPost f as
-- | A version of 'hoist' that works for arbitrary functions, rather
-- than just monad morphisms.
hoistPreUnexposed :: forall m n a. Monad m => (forall x. m x -> n x) -> SeqT m a -> SeqT n a
hoistPreUnexposed f (toViewT -> m) = fromViewT $ f (myliftM go m)
where
go Empty = Empty
go (a :< as) = a :< hoistPreUnexposed f as
-- | A version of 'hoistPost' that works for arbitrary functions, rather
-- than just monad morphisms. This should be preferred when the `Monad` instance
-- for `n` is less expensive than that for `m`.
hoistPostUnexposed :: forall m n a. (Monad m, Monad n) => (forall x. m x -> n x) -> SeqT m a -> SeqT n a
hoistPostUnexposed f (toViewT -> m) = fromViewT $ myliftM go (f m)
where
go Empty = Empty
go (a :< as) = a :< hoistPostUnexposed f as
instance MonadIO m => MonadIO (SeqT m) where
{-# INLINE liftIO #-}
liftIO = lift . liftIO
instance MonadReader e m => MonadReader e (SeqT m) where
-- TODO: write more thorough tests for this instance (issue #31)
ask = lift ask
local f (SeqT q) = SeqT $ fmap (local f . myliftM go) q
where
go Empty = Empty
go (a :< s) = a :< local f s
instance MonadState s m => MonadState s (SeqT m) where
get = lift get
put = lift . put
state = lift . state
instance MonadError e m => MonadError e (SeqT m) where
-- TODO: write tests for this instance (issue #31)
throwError = lift . throwError
catchError (toViewT -> m) h = fromViewT $ (myliftM go m) `catchError` (toViewT . h)
where
go Empty = Empty
go (a :< s) = a :< catchError s h
#if MIN_VERSION_base(4,8,0)
instance MonadZip m => MonadZip (SeqT m) where
mzipWith f (toViewT -> m) (toViewT -> n) = fromViewT $
mzipWith go m n
where
go (a :< as) (b :< bs) = f a b :< mzipWith f as bs
go _ _ = Empty
munzip (toViewT -> m)
| (l, r) <- munzip (fmap go m) = (fromViewT l, fromViewT r)
where
go Empty = (Empty, Empty)
go ((a, b) :< asbs) = (a :< as, b :< bs)
where
-- We want to be lazy so we don't force the entire
-- structure unnecessarily. But we don't want to introduce
-- a space leak, so we're careful to create selector thunks
-- to deconstruct the rest of the chain.
{-# NOINLINE muabs #-}
{-# NOINLINE as #-}
{-# NOINLINE bs #-}
muabs = munzip asbs
(as, bs) = muabs
#endif
#if MIN_VERSION_base(4,8,0)
myliftM :: Functor m => (a -> b) -> m a -> m b
myliftM = fmap
#else
myliftM :: Monad m => (a -> b) -> m a -> m b
myliftM = Monad.liftM
#endif
{-# INLINE myliftM #-}