packages feed

ideas-0.6: src/Domain/Math/Data/OrList.hs

-----------------------------------------------------------------------------
-- Copyright 2010, Open Universiteit Nederland. This file is distributed 
-- under the terms of the GNU General Public License. For more information, 
-- see the file "LICENSE.txt", which is included in the distribution.
-----------------------------------------------------------------------------
-- |
-- Maintainer  :  bastiaan.heeren@ou.nl
-- Stability   :  provisional
-- Portability :  portable (depends on ghc)
--
-----------------------------------------------------------------------------
module Domain.Math.Data.OrList 
   ( OrList
   , orList, (\/), true, false
   , isTrue, isFalse
   , disjunctions, normalize, idempotent
   , orView
   ) where

import Common.View
import Control.Monad
import Common.Traversable
import Common.Rewriting.Term
import qualified Domain.Logic.Formula as Logic
import Domain.Logic.Formula (Logic((:||:)))
import Test.QuickCheck
import Data.List (intersperse, nub, sort)

------------------------------------------------------------
-- Data type

data OrList a = T | OrList [a] 
   deriving (Ord, Eq)

------------------------------------------------------------
-- Functions

orList :: [a] -> OrList a
orList = OrList

true, false :: OrList a
true  = T
false = OrList []

isTrue :: OrList a -> Bool
isTrue T = Prelude.True
isTrue _ = False

isFalse :: OrList a -> Bool
isFalse (OrList []) = True
isFalse _           = False

disjunctions :: OrList a -> Maybe [a]
disjunctions T           = Nothing
disjunctions (OrList xs) = Just xs

(\/) :: OrList a -> OrList a -> OrList a
p \/ q = maybe T orList (liftM2 (++) (disjunctions p) (disjunctions q))

-- | Sort the propositions and remove duplicates
normalize :: Ord a => OrList a -> OrList a
normalize T           = T
normalize (OrList xs) = OrList (nub $ sort xs)

-- | Remove duplicates
idempotent :: Eq a => OrList a -> OrList a
idempotent T           = T
idempotent (OrList xs) = OrList (nub xs)

------------------------------------------------------------
-- Instances

-- local helper
joinOr :: OrList (OrList a) -> OrList a
joinOr = maybe T (foldr (\/) false) . disjunctions

instance Functor OrList where
   fmap _ T           = T
   fmap f (OrList xs) = OrList (map f xs)

instance Monad OrList where
   return  = OrList . return
   m >>= f = joinOr (fmap f m)

instance Once OrList where
   onceM = useOnceJoin

instance Switch OrList where
   switch T           = return T
   switch (OrList xs) = liftM orList (sequence xs)

instance Crush OrList where
   crush T           = []
   crush (OrList xs) = xs

instance OnceJoin OrList where
   onceJoinM _ T = mzero
   onceJoinM f (OrList xs) = rec xs
    where
      rec []     = mzero
      rec (x:xs) = liftM (\/ orList xs) (f x) `mplus`
                   liftM (return x \/) (rec xs)

instance IsTerm a => IsTerm (OrList a) where
   toTerm = toTerm . build orView
   fromTerm expr = fromTerm expr >>= matchM orView

instance Arbitrary a => Arbitrary (OrList a) where
   arbitrary = do 
      n  <- choose (1, 3)
      xs <- vector n
      return (OrList xs)
instance CoArbitrary a => CoArbitrary (OrList a) where
   coarbitrary T           = variant 0
   coarbitrary (OrList xs) = variant 1 . coarbitrary xs

instance Show a => Show (OrList a) where
   show T = "true"
   show (OrList xs) 
      | null xs   = "false"
      | otherwise = unwords (intersperse "or" (map show xs))

------------------------------------------------------------
-- View to the logic data type
 
orView :: View (Logic a) (OrList a)
orView = makeView f g 
 where
   f p  = case p of
             Logic.Var a -> return (return a)
             Logic.T     -> return true
             Logic.F     -> return false
             a :||: b    -> liftM2 (\/) (f a) (f b)
             _           -> Nothing
   g xs = case disjunctions xs of
             Nothing -> Logic.T
             Just [] -> Logic.F
             Just ys -> foldr1 (:||:) (map Logic.Var ys)