The basic Logic monad, for performing backtracking computations
returning values (e.g. Logic a will return values of type a).
It's important to remember that Logic on its own is just
a lawful list monad, behaving exactly as instance Monad [].
One should explicitly use methods of MonadLogic such as (>>-) and interleave
to get fair conjunction / disjunction. Note that usual
lists have an instance of MonadLogic, so maybe you don't need Logic at all.
Technical perspective.
Logic is a
Boehm-Berarducci encoding
of lists. Speaking plainly, its type is identical (up to Identity wrappers)
to Data.List.foldr applied to a given list. And this list itself can be reconstructed
by supplying (:) and [].
import Data.Functor.Identity
fromList :: [a] -> Logic a
fromList xs = LogicT $ \cons nil -> foldr cons nil xs
toList :: Logic a -> [a]
toList (LogicT fld) = runIdentity $ fld (\x (Identity xs) -> Identity (x : xs)) (Identity [])Here is a systematic derivation of the isomorphism. We start with observing
that [a] is isomorphic to a fix point of a non-recursive
base algebra Fix (ListF a):
newtype Fix f = Fix (f (Fix f))
data ListF a r = ConsF a r | NilF deriving (Functor)
cata :: Functor f => (f r -> r) -> Fix f -> r
cata f = go where go (Fix x) = f (fmap go x)
from :: [a] -> Fix (ListF a)
from = foldr (\a acc -> Fix (ConsF a acc)) (Fix NilF)
to :: Fix (ListF a) -> [a]
to = cata (\case ConsF a r -> a : r; NilF -> [])Further, Fix (ListF a) is isomorphic to Boehm-Berarducci encoding ListC a:
newtype ListC a = ListC (forall r. (ListF a r -> r) -> r)
from :: Fix (ListF a) -> ListC a
from xs = ListC (\f -> cata f xs)
to :: ListC a -> Fix (ListF a)
to (ListC f) = f FixFinally, ListF a r → r is isomorphic to a pair (a → r → r, r),
so ListC is isomorphic to the Logic type modulo Identity wrappers:
newtype Logic a = Logic (forall r. (a -> r -> r) -> r -> r)And wrapping every occurence of r into m gives us LogicT:
newtype LogicT m a = Logic (forall r. (a -> m r -> m r) -> m r -> m r)