logict-0.8.0.0: A backtracking logic-programming monad.
Copyright (c) 2007-2014 Dan Doel
(c) 2011-2013 Edward Kmett
(c) 2014 Roman Cheplyaka
(c) 2020-2021 Andrew Lelechenko
(c) 2020-2021 Kevin Quick
License BSD3
Maintainer Andrew Lelechenko <andrew.lelechenko@gmail.com>
Safe Haskell Safe
Language Haskell2010

Control.Monad.Logic

Description

Adapted from the paper Backtracking, Interleaving, and Terminating Monad Transformers by Oleg Kiselyov, Chung-chieh Shan, Daniel P. Friedman, Amr Sabry. Note that the paper uses MonadPlus vocabulary ( mzero and mplus ), while examples below prefer empty and <|> from Alternative .

Synopsis

Documentation

The Logic monad

type Logic = LogicT Identity Source #

The basic Logic monad, for performing backtracking computations returning values (e.g. Logic a will return values of type a ).

Technical perspective. Logic is a Boehm-Berarducci encoding of lists. Speaking plainly, its type is identical (up to Identity wrappers) to 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 [])

logic :: ( forall r. (a -> r -> r) -> r -> r) -> Logic a Source #

A smart constructor for Logic computations.

runLogic :: Logic a -> (a -> r -> r) -> r -> r Source #

Runs a Logic computation with the specified initial success and failure continuations.

>>> runLogic empty (+) 0
0
>>> runLogic (pure 5 <|> pure 3 <|> empty) (+) 0
8

When invoked with (:) and [] as arguments, reveals a half of the isomorphism between Logic and lists. See description of observeAll for the other half.

observe :: Logic a -> a Source #

Extracts the first result from a Logic computation, failing if there are no results.

>>> observe (pure 5 <|> pure 3 <|> empty)
5
>>> observe empty
*** Exception: No answer.

Since Logic is isomorphic to a list, observe is analogous to head .

observeMany :: Int -> Logic a -> [a] Source #

Extracts up to a given number of results from a Logic computation.

>>> let nats = pure 0 <|> fmap (+ 1) nats
>>> observeMany 5 nats
[0,1,2,3,4]

Since Logic is isomorphic to a list, observeMany is analogous to take .

observeAll :: Logic a -> [a] Source #

Extracts all results from a Logic computation.

>>> observeAll (pure 5 <|> empty <|> empty <|> pure 3 <|> empty)
[5,3]

observeAll reveals a half of the isomorphism between Logic and lists. See description of runLogic for the other half.

The LogicT monad transformer

newtype LogicT m a Source #

A monad transformer for performing backtracking computations layered over another monad m .

When m is Identity , LogicT m becomes isomorphic to a list (see Logic ). Thus LogicT m for non-trivial m can be imagined as a list, pattern matching on which causes monadic effects.

Constructors

LogicT

Fields

  • unLogicT :: forall r. (a -> m r -> m r) -> m r -> m r

Instances

Instances details
MonadTrans LogicT Source #
Instance details

Defined in Control.Monad.Logic

Methods

lift :: Monad m => m a -> LogicT m a Source #

MonadState s m => MonadState s ( LogicT m) Source #
Instance details

Defined in Control.Monad.Logic

MonadReader r m => MonadReader r ( LogicT m) Source #
Instance details

Defined in Control.Monad.Logic

MonadError e m => MonadError e ( LogicT m) Source #
Instance details

Defined in Control.Monad.Logic

Monad ( LogicT m) Source #
Instance details

Defined in Control.Monad.Logic

Functor ( LogicT f) Source #
Instance details

Defined in Control.Monad.Logic

Methods

fmap :: (a -> b) -> LogicT f a -> LogicT f b Source #

(<$) :: a -> LogicT f b -> LogicT f a Source #

MonadFail ( LogicT m) Source #
Instance details

Defined in Control.Monad.Logic

Applicative ( LogicT f) Source #
Instance details

Defined in Control.Monad.Logic

( Applicative m, Foldable m) => Foldable ( LogicT m) Source #
Instance details

Defined in Control.Monad.Logic

Foldable ( LogicT Identity ) Source #
Instance details

Defined in Control.Monad.Logic

( Monad m, Traversable m) => Traversable ( LogicT m) Source #
Instance details

Defined in Control.Monad.Logic

Traversable ( LogicT Identity ) Source #
Instance details

Defined in Control.Monad.Logic

MonadZip m => MonadZip ( LogicT m) Source #
Instance details

Defined in Control.Monad.Logic

MonadIO m => MonadIO ( LogicT m) Source #
Instance details

Defined in Control.Monad.Logic

Alternative ( LogicT f) Source #
Instance details

Defined in Control.Monad.Logic

MonadPlus ( LogicT m) Source #
Instance details

Defined in Control.Monad.Logic

Monad m => MonadLogic ( LogicT m) Source #
Instance details

Defined in Control.Monad.Logic

Semigroup ( LogicT m a) Source #
Instance details

Defined in Control.Monad.Logic

Monoid ( LogicT m a) Source #
Instance details

Defined in Control.Monad.Logic

runLogicT :: LogicT m a -> (a -> m r -> m r) -> m r -> m r Source #

Runs a LogicT computation with the specified initial success and failure continuations.

The second argument ("success continuation") takes one result of the LogicT computation and the monad to run for any subsequent matches.

The third argument ("failure continuation") is called when the LogicT cannot produce any more results.

For example:

>>> yieldWords = foldr ((<|>) . pure) empty
>>> showEach wrd nxt = putStrLn wrd >> nxt
>>> runLogicT (yieldWords ["foo", "bar"]) showEach (putStrLn "none!")
foo
bar
none!
>>> runLogicT (yieldWords []) showEach (putStrLn "none!")
none!
>>> showFirst wrd _ = putStrLn wrd
>>> runLogicT (yieldWords ["foo", "bar"]) showFirst (putStrLn "none!")
foo

observeT :: MonadFail m => LogicT m a -> m a Source #

Extracts the first result from a LogicT computation, failing if there are no results at all.

observeManyT :: Monad m => Int -> LogicT m a -> m [a] Source #

Extracts up to a given number of results from a LogicT computation.

observeAllT :: Applicative m => LogicT m a -> m [a] Source #

Extracts all results from a LogicT computation, unless blocked by the underlying monad.

For example, given

>>> let nats = pure 0 <|> fmap (+ 1) nats

some monads (like Identity , Reader , Writer , and State ) will be productive:

>>> take 5 $ runIdentity (observeAllT nats)
[0,1,2,3,4]

but others (like ExceptT , and ContT ) will not:

>>> take 20 <$> runExcept (observeAllT nats)

In general, if the underlying monad manages control flow then observeAllT may be unproductive under infinite branching, and observeManyT should be used instead.

fromLogicT :: ( Alternative (t m), MonadTrans t, Monad m, Monad (t m)) => LogicT m a -> t m a Source #

Convert from LogicT to an arbitrary logic-like monad transformer, such as list-t or logict-sequence

For example, to show a representation of the structure of a LogicT computation, l , over a data-like Monad (such as [] , Data.Sequence.Seq , etc.), you could write

import ListT (ListT)

show $ fromLogicT @ListT l

fromLogicTWith :: ( Applicative m, Monad n, Alternative n) => ( forall x. m x -> n x) -> LogicT m a -> n a Source #

Convert from LogicT m to an arbitrary logic-like monad, such as [] .

Examples:

fromLogicT = fromLogicTWith d
hoistLogicT f = fromLogicTWith (lift . f)
embedLogicT f = fromLogicTWith f

The first argument should be a monad morphism . to produce sensible results.

hoistLogicT :: ( Applicative m, Monad n) => ( forall x. m x -> n x) -> LogicT m a -> LogicT n a Source #

Convert a LogicT computation from one underlying monad to another. For example,

hoistLogicT lift :: LogicT m a -> LogicT (StateT m) a

The first argument should be a monad morphism . to produce sensible results.

embedLogicT :: Applicative m => ( forall a. m a -> LogicT n a) -> LogicT m b -> LogicT n b Source #

Convert a LogicT computation from one underlying monad to another.

The first argument should be a monad morphism . to produce sensible results.