----------------------------------------------------------------------
-- |
-- Module      : BacktrackM
-- Maintainer  : PL
-- Stability   : (stable)
-- Portability : (portable)
--
-- > CVS $Date: 2005/04/21 16:22:00 $
-- > CVS $Author: bringert $
-- > CVS $Revision: 1.4 $
--
-- Backtracking state monad, with r\/o environment
-----------------------------------------------------------------------------

{-# LANGUAGE Rank2Types, MultiParamTypeClasses, FlexibleInstances #-}
module GF.Data.BacktrackM (
                    -- * the backtracking state monad
		    BacktrackM,
		    -- * monad specific utilities
		    member,
		    cut,
		    -- * running the monad
		    foldBM,          runBM,
		    foldSolutions,   solutions,
		    foldFinalStates, finalStates,
		    
		    -- * reexport the 'MonadState' class
		    module Control.Monad.State.Class,
		  ) where

import Data.List
import Control.Applicative
import Control.Monad
import Control.Monad.State.Class

----------------------------------------------------------------------
-- Combining endomorphisms and continuations
-- a la Ralf Hinze

-- BacktrackM = state monad transformer over the backtracking monad

newtype BacktrackM s a = BM (forall b . (a -> s -> b -> b) -> s -> b -> b)

-- * running the monad

runBM :: BacktrackM s a -> s -> [(s,a)]
runBM (BM m) s = m (\x s xs -> (s,x) : xs) s []

foldBM :: (a -> s -> b -> b) -> b -> BacktrackM s a -> s -> b
foldBM f b (BM m) s = m f s b

foldSolutions   :: (a -> b -> b) -> b -> BacktrackM s a -> s -> b
foldSolutions f b (BM m) s = m (\x s b -> f x b) s b

solutions   :: BacktrackM s a  -> s -> [a]
solutions = foldSolutions (:) []

foldFinalStates :: (s -> b -> b) -> b -> BacktrackM s () -> s -> b
foldFinalStates f b (BM m) s = m (\x s b -> f s b) s b

finalStates :: BacktrackM s () -> s -> [s]
finalStates bm = map fst . runBM bm

instance Applicative (BacktrackM s) where
    pure = return
    (<*>) = ap

instance Monad (BacktrackM s) where
    return a   = BM (\c s b -> c a s b)
    BM m >>= k = BM (\c s b -> m (\a s b -> unBM (k a) c s b) s b)
	where unBM (BM m) = m
    fail _ = mzero

instance Functor (BacktrackM s) where
    fmap f (BM m) = BM (\c s b -> m (\a s b -> c (f a) s b) s b)

instance Alternative (BacktrackM s) where
   empty = mzero
   (<|>) = mplus

instance MonadPlus (BacktrackM s) where
    mzero                 = BM (\c s b -> b)
    (BM f) `mplus` (BM g) = BM (\c s b -> g c s $! f c s b)

instance MonadState s (BacktrackM s) where
  get = BM (\c s b -> c s s b)
  put s = BM (\c _ b -> c () s b)

-- * specific functions on the backtracking monad

member :: [a] -> BacktrackM s a
member xs = BM (\c s b -> foldl' (\b x -> c x s b) b xs)

cut :: BacktrackM s a -> BacktrackM s [(s,a)]
cut f = BM (\c s b -> c (runBM f s) s b)
