% Constraint Functional-Logic Programming
% Sebastian Fischer (sebf@informatik.uni-kiel.de)

This module provides an interface that can be used for constraint
functional-logic programming in Haskell.

> {-# LANGUAGE
>       GeneralizedNewtypeDeriving,
>       MultiParamTypeClasses,
>       FlexibleInstances,
>       FlexibleContexts,
>       TypeFamilies,
>       RankNTypes
>   #-}
>
> module CFLP (
>
>   CFLP, Enumerable(..), Ctx, Data, Computation,
>
>   eval, evalPartial, evalPrint,
>
>   Monadic, monadic, UpdateT,
>
>   module CFLP.Data
>
> ) where
>
> import Control.Monad.State
>
> import CFLP.Data
> import CFLP.Data.Types
>
> import CFLP.Control.Monad.Update
>
> import CFLP.Control.Strategy

The type class `CFLP` amalgamates all class constraints on constraint
functional-logic computations.

> class (Strategy (Ctx s) s, MonadPlus s
>                          , MonadUpdate (Ctx s) s
>                          , Update (Ctx s) s s
>                          , Update (Ctx s) s (Res s)
>                          , MonadPlus (Res s)
>                          , Enumerable (Res s))
>    => CFLP s
>
> monadic :: Monad m => Monadic (UpdateT c m) ()
> monadic = Monadic (return ())
>
> instance (MonadPlus m, Enumerable m) => CFLP (Monadic (UpdateT () m))

We define a shortcut for types of constraint functional-logic data and
computations that can be parameterized by an arbitrary strategy.

> type Data s a = Nondet (Ctx s) s a
>
> type Computation a
>   = forall s . CFLP s => Context (Ctx s) -> ID -> Data s a

We provide

  * an `eval` operation to compute Haskell terms from
    non-deterministic data,

  * an operation `evalPartial` to compute partial Haskell terms where
    logic variables are replaced with an error, and

  * an `evalPrint` operation that interactively shows (partial)
    solutions of a constraint functional-logic computation.

> eval, evalPartial
>   :: (Monad s, CFLP s, Generic a) => [s (Ctx s)] -> Computation a -> IO [a]
> eval        s = liftM (liftM primitive) . evaluate s groundNormalForm
> evalPartial s = liftM (liftM primitive) . evaluate s partialNormalForm
>
> evalPrint :: (Monad s, CFLP s, Generic a)
>           => [s (Ctx s)] -> Computation a -> IO ()
> evalPrint s op = evaluate s partialNormalForm op >>= printSols
>
> printSols :: Show a => [a] -> IO ()
> printSols []     = putStrLn "No more solutions."
> printSols (x:xs) = do
>   print x
>   putStr "more? [Y(es)|n(o)|a(ll)]: "
>   s <- getLine
>   if s `elem` ["n","no"] then
>     return ()
>    else if s `elem` ["a","all"]
>     then mapM_ print xs
>     else printSols xs

The `evaluate` function enumerates the non-deterministic solutions of a
constraint functional-logic computation according to a given strategy.

> evaluate :: CFLP s
>          => [s (Ctx s)]
>          -> (s (Ctx s) -> Nondet (Ctx s) s a -> Res s b)
>          -> Computation a
>          -> IO [b]
> evaluate s evalNondet op = do
>   i <- initID
>   return $ concatMap enumeration $
>     map (\c -> evalNondet c (Typed (c >>= untyped . flip op i . Context))) s