atp-haskell-1.7: src/Data/Logic/ATP/Lit.hs
-- | 'IsLiteral' is a subclass of formulas that support negation and
-- have true and false elements.
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE EmptyDataDecls #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
module Data.Logic.ATP.Lit
( IsLiteral(naiveNegate, foldNegation, foldLiteral')
, (.~.), (¬), negate
, negated
, negative, positive
, foldLiteral
, JustLiteral
, onatomsLiteral
, overatomsLiteral
, zipLiterals', zipLiterals
, convertLiteral
, convertToLiteral
, precedenceLiteral
, associativityLiteral
, prettyLiteral
, showLiteral
-- * Instance
, LFormula(T, F, Atom, Not)
, Lit(L, lname)
) where
import Data.Data (Data)
import Data.Logic.ATP.Formulas (IsAtom, IsFormula(atomic, AtomOf, asBool, false, true), fromBool, overatoms, onatoms, prettyBool)
import Data.Logic.ATP.Pretty (Associativity(..), boolPrec, Doc, HasFixity(precedence, associativity), notPrec, Precedence, text)
import Data.Monoid ((<>))
import Data.Typeable (Typeable)
import Prelude hiding (negate, null)
import Text.PrettyPrint.HughesPJClass (maybeParens, Pretty(pPrint, pPrintPrec), PrettyLevel, prettyNormal)
-- | The class of formulas that can be negated. Literals are the
-- building blocks of the clause and implicative normal forms. They
-- support negation and must include true and false elements.
class IsFormula lit => IsLiteral lit where
-- | Negate a formula in a naive fashion, the operators below
-- prevent double negation.
naiveNegate :: lit -> lit
-- | Test whether a lit is negated or normal
foldNegation :: (lit -> r) -- ^ called for normal formulas
-> (lit -> r) -- ^ called for negated formulas
-> lit -> r
-- | This is the internal fold for literals, 'foldLiteral' below should
-- normally be used, but its argument must be an instance of 'JustLiteral'.
foldLiteral' :: (lit -> r) -- ^ Called for higher order formulas (non-literal)
-> (lit -> r) -- ^ Called for negated formulas
-> (Bool -> r) -- ^ Called for true and false formulas
-> (AtomOf lit -> r) -- ^ Called for atomic formulas
-> lit -> r
-- | Is this formula negated at the top level?
negated :: IsLiteral formula => formula -> Bool
negated = foldNegation (const False) (not . negated)
-- | Negate the formula, avoiding double negation
(.~.), (¬), negate :: IsLiteral formula => formula -> formula
(.~.) = foldNegation naiveNegate id
(¬) = (.~.)
negate = (.~.)
infix 6 .~., ¬
-- | Some operations on IsLiteral formulas
negative :: IsLiteral formula => formula -> Bool
negative = negated
positive :: IsLiteral formula => formula -> Bool
positive = not . negative
foldLiteral :: JustLiteral lit => (lit -> r) -> (Bool -> r) -> (AtomOf lit -> r) -> lit -> r
foldLiteral = foldLiteral' (error "JustLiteral failure")
-- | Class that indicates that a formula type *only* contains 'IsLiteral'
-- features - no combinations or quantifiers.
class IsLiteral formula => JustLiteral formula
-- | Combine two literals (internal version).
zipLiterals' :: (IsLiteral lit1, IsLiteral lit2) =>
(lit1 -> lit2 -> Maybe r)
-> (lit1 -> lit2 -> Maybe r)
-> (Bool -> Bool -> Maybe r)
-> (AtomOf lit1 -> AtomOf lit2 -> Maybe r)
-> lit1 -> lit2 -> Maybe r
zipLiterals' ho neg tf at fm1 fm2 =
foldLiteral' ho' neg' tf' at' fm1
where
ho' x1 = foldLiteral' (ho x1) (\ _ -> Nothing) (\ _ -> Nothing) (\ _ -> Nothing) fm2
neg' p1 = foldLiteral' (\ _ -> Nothing) (neg p1) (\ _ -> Nothing) (\ _ -> Nothing) fm2
tf' x1 = foldLiteral' (\ _ -> Nothing) (\ _ -> Nothing) (tf x1) (\ _ -> Nothing) fm2
at' a1 = foldLiteral' (\ _ -> Nothing) (\ _ -> Nothing) (\ _ -> Nothing) (at a1) fm2
-- | Combine two literals.
zipLiterals :: (JustLiteral lit1, JustLiteral lit2) =>
(lit1 -> lit2 -> Maybe r)
-> (Bool -> Bool -> Maybe r)
-> (AtomOf lit1 -> AtomOf lit2 -> Maybe r)
-> lit1 -> lit2 -> Maybe r
zipLiterals neg tf at fm1 fm2 =
foldLiteral neg' tf' at' fm1
where
neg' p1 = foldLiteral (neg p1) (\ _ -> Nothing) (\ _ -> Nothing) fm2
tf' x1 = foldLiteral (\ _ -> Nothing) (tf x1) (\ _ -> Nothing) fm2
at' a1 = foldLiteral (\ _ -> Nothing) (\ _ -> Nothing) (at a1) fm2
-- | Convert a 'JustLiteral' instance to any 'IsLiteral' instance.
convertLiteral :: (JustLiteral lit1, IsLiteral lit2) => (AtomOf lit1 -> AtomOf lit2) -> lit1 -> lit2
convertLiteral ca fm = foldLiteral (\fm' -> (.~.) (convertLiteral ca fm')) fromBool (atomic . ca) fm
-- | Convert any formula to a literal, passing non-IsLiteral
-- structures to the first argument (typically a call to error.)
convertToLiteral :: (IsLiteral formula, JustLiteral lit) =>
(formula -> lit) -> (AtomOf formula -> AtomOf lit) -> formula -> lit
convertToLiteral ho ca fm = foldLiteral' ho (\fm' -> (.~.) (convertToLiteral ho ca fm')) fromBool (atomic . ca) fm
precedenceLiteral :: JustLiteral lit => lit -> Precedence
precedenceLiteral = foldLiteral (const notPrec) (const boolPrec) precedence
associativityLiteral :: JustLiteral lit => lit -> Associativity
associativityLiteral = foldLiteral (const InfixA) (const InfixN) associativity
-- | Implementation of 'pPrint' for -- 'JustLiteral' types.
prettyLiteral :: JustLiteral lit => PrettyLevel -> Rational -> lit -> Doc
prettyLiteral l r lit =
maybeParens (l > prettyNormal || r > precedence lit) (foldLiteral ne tf at lit)
where
ne p = text "¬" <> prettyLiteral l (precedence lit) p
tf = prettyBool
at a = pPrint a
showLiteral :: JustLiteral lit => lit -> String
showLiteral lit = foldLiteral ne tf at lit
where
ne p = "(.~.)(" ++ showLiteral p ++ ")"
tf = show
at = show
-- | Implementation of 'onatoms' for 'JustLiteral' types.
onatomsLiteral :: JustLiteral lit => (AtomOf lit -> AtomOf lit) -> lit -> lit
onatomsLiteral f fm =
foldLiteral ne tf at fm
where
ne p = (.~.) (onatomsLiteral f p)
tf = fromBool
at x = atomic (f x)
-- | implementation of 'overatoms' for 'JustLiteral' types.
overatomsLiteral :: JustLiteral lit => (AtomOf lit -> r -> r) -> lit -> r -> r
overatomsLiteral f fm r0 =
foldLiteral ne (const r0) (flip f r0) fm
where
ne fm' = overatomsLiteral f fm' r0
-- | Example of a 'JustLiteral' type.
data LFormula atom
= F
| T
| Atom atom
| Not (LFormula atom)
deriving (Eq, Ord, Read, Show, Data, Typeable)
data Lit = L {lname :: String} deriving (Eq, Ord)
instance IsAtom atom => IsFormula (LFormula atom) where
type AtomOf (LFormula atom) = atom
asBool T = Just True
asBool F = Just False
asBool _ = Nothing
true = T
false = F
atomic = Atom
overatoms = overatomsLiteral
onatoms = onatomsLiteral
instance (IsFormula (LFormula atom), Eq atom, Ord atom) => IsLiteral (LFormula atom) where
naiveNegate = Not
foldNegation normal inverted (Not x) = foldNegation inverted normal x
foldNegation normal _ x = normal x
foldLiteral' _ ne tf at lit =
case lit of
F -> tf False
T -> tf True
Atom a -> at a
Not f -> ne f
instance IsAtom atom => JustLiteral (LFormula atom)
instance IsAtom atom => HasFixity (LFormula atom) where
precedence = precedenceLiteral
associativity = associativityLiteral
instance IsAtom atom => Pretty (LFormula atom) where
pPrintPrec = prettyLiteral