module-management-0.20.2: testdata/split-merge-expected/Data/Logic/Classes/FirstOrder.hs
{-# LANGUAGE DeriveDataTypeable, FlexibleContexts, FlexibleInstances, FunctionalDependencies,
MultiParamTypeClasses, RankNTypes, ScopedTypeVariables, TemplateHaskell, UndecidableInstances #-}
module Data.Logic.Classes.FirstOrder
( FirstOrderFormula(..)
, Quant(..)
, zipFirstOrder
, pApp
, pApp0
, pApp1
, pApp2
, pApp3
, pApp4
, pApp5
, pApp6
, pApp7
, for_all'
, exists'
, quant
, (!)
, (?)
, (∀)
, (∃)
, quant'
, convertFOF
, toPropositional
, withUnivQuants
, showFirstOrder
, prettyFirstOrder
, fixityFirstOrder
, foldAtomsFirstOrder
, mapAtomsFirstOrder
, onatoms
, overatoms
, atom_union
, fromFirstOrder
, fromLiteral
) where
import Data.Generics (Data, Typeable)
import Data.Logic.Classes.Apply (apply, Apply(..), apply0, apply1, apply2, apply3, apply4, apply5, apply6, apply7)
import Data.Logic.Classes.Combine (binop, BinOp((:&:), (:<=>:), (:=>:), (:|:)), Combinable, Combination(..), combine)
import Data.Logic.Classes.Constants (Constants(fromBool), ifElse)
import Data.Logic.Classes.Formula (Formula(atomic))
import Data.Logic.Classes.Literal.Literal (Literal(foldLiteral))
import Data.Logic.Classes.Negate ((.~.))
import Data.Logic.Classes.Pretty (Fixity(..), FixityDirection(..), HasFixity(..), Pretty(pretty))
import qualified Data.Logic.Classes.Propositional as P (PropositionalFormula)
import Data.Logic.Classes.Variable (Variable)
import Data.Logic.Failing (Failing(..))
import Data.SafeCopy (base, deriveSafeCopy)
import qualified Data.Set as Set (empty, Set, union)
import Text.PrettyPrint ((<+>), (<>), Doc, nest, parens, text)
-- |The 'FirstOrderFormula' type class. Minimal implementation:
-- @for_all, exists, foldFirstOrder, foldTerm, (.=.), pApp0-pApp7, fApp, var@. The
-- functional dependencies are necessary here so we can write
-- functions that don't fix all of the type parameters. For example,
-- without them the univquant_free_vars function gives the error @No
-- instance for (FirstOrderFormula Formula atom V)@ because the
-- function doesn't mention the Term type.
class ( Formula formula atom
, Combinable formula -- Basic logic operations
, Constants formula
, Constants atom
, HasFixity atom
, Variable v
, Pretty atom, Pretty v
) => FirstOrderFormula formula atom v | formula -> atom v where
-- | Universal quantification - for all x (formula x)
for_all :: v -> formula -> formula
-- | Existential quantification - there exists x such that (formula x)
exists :: v -> formula -> formula
-- | A fold function similar to the one in 'PropositionalFormula'
-- but extended to cover both the existing formula types and the
-- ones introduced here. @foldFirstOrder (.~.) quant binOp infixPred pApp@
-- is a no op. The argument order is taken from Logic-TPTP.
foldFirstOrder :: (Quant -> v -> formula -> r)
-> (Combination formula -> r)
-> (Bool -> r)
-> (atom -> r)
-> formula
-> r
zipFirstOrder :: FirstOrderFormula formula atom v =>
(Quant -> v -> formula -> Quant -> v -> formula -> Maybe r)
-> (Combination formula -> Combination formula -> Maybe r)
-> (Bool -> Bool -> Maybe r)
-> (atom -> atom -> Maybe r)
-> formula -> formula -> Maybe r
zipFirstOrder qu co tf at fm1 fm2 =
foldFirstOrder qu' co' tf' at' fm1
where
qu' op1 v1 p1 = foldFirstOrder (qu op1 v1 p1) (\ _ -> Nothing) (\ _ -> Nothing) (\ _ -> Nothing) fm2
co' c1 = foldFirstOrder (\ _ _ _ -> Nothing) (co c1) (\ _ -> Nothing) (\ _ -> Nothing) fm2
tf' x1 = foldFirstOrder (\ _ _ _ -> Nothing) (\ _ -> Nothing) (tf x1) (\ _ -> Nothing) fm2
at' atom1 = foldFirstOrder (\ _ _ _ -> Nothing) (\ _ -> Nothing) (\ _ -> Nothing) (at atom1) fm2
-- |The 'Quant' and 'InfixPred' types, like the BinOp type in
-- 'Data.Logic.Propositional', could be additional parameters to the type
-- class, but it would add additional complexity with unclear
-- benefits.
data Quant = Forall | Exists deriving (Eq,Ord,Show,Read,Data,Typeable,Enum,Bounded)
pApp :: forall formula atom term v p. (FirstOrderFormula formula atom v, Apply atom p term) => p -> [term] -> formula
pApp p ts = atomic (apply p ts :: atom)
-- | Versions of pApp specialized for different argument counts.
pApp0 :: forall formula atom term v p. (FirstOrderFormula formula atom v, Apply atom p term) => p -> formula
pApp0 p = atomic (apply0 p :: atom)
pApp1 :: forall formula atom term v p. (FirstOrderFormula formula atom v, Apply atom p term) => p -> term -> formula
pApp1 p a = atomic (apply1 p a :: atom)
pApp2 :: forall formula atom term v p. (FirstOrderFormula formula atom v, Apply atom p term) => p -> term -> term -> formula
pApp2 p a b = atomic (apply2 p a b :: atom)
pApp3 :: forall formula atom term v p. (FirstOrderFormula formula atom v, Apply atom p term) => p -> term -> term -> term -> formula
pApp3 p a b c = atomic (apply3 p a b c :: atom)
pApp4 :: forall formula atom term v p. (FirstOrderFormula formula atom v, Apply atom p term) => p -> term -> term -> term -> term -> formula
pApp4 p a b c d = atomic (apply4 p a b c d :: atom)
pApp5 :: forall formula atom term v p. (FirstOrderFormula formula atom v, Apply atom p term) => p -> term -> term -> term -> term -> term -> formula
pApp5 p a b c d e = atomic (apply5 p a b c d e :: atom)
pApp6 :: forall formula atom term v p. (FirstOrderFormula formula atom v, Apply atom p term) => p -> term -> term -> term -> term -> term -> term -> formula
pApp6 p a b c d e f = atomic (apply6 p a b c d e f :: atom)
pApp7 :: forall formula atom term v p. (FirstOrderFormula formula atom v, Apply atom p term) => p -> term -> term -> term -> term -> term -> term -> term -> formula
pApp7 p a b c d e f g = atomic (apply7 p a b c d e f g :: atom)
-- |for_all with a list of variables, for backwards compatibility.
for_all' :: FirstOrderFormula formula atom v => [v] -> formula -> formula
for_all' vs f = foldr for_all f vs
-- |exists with a list of variables, for backwards compatibility.
exists' :: FirstOrderFormula formula atom v => [v] -> formula -> formula
exists' vs f = foldr for_all f vs
-- |Names for for_all and exists inspired by the conventions of the
-- TPTP project.
(!) :: FirstOrderFormula formula atom v => v -> formula -> formula
(!) = for_all
(?) :: FirstOrderFormula formula atom v => v -> formula -> formula
(?) = exists
-- Irrelevant, because these are always used as prefix operators, never as infix.
infixr 9 !, ?, ∀, ∃
-- | ∀ can't be a function when -XUnicodeSyntax is enabled.
(∀) :: FirstOrderFormula formula atom v => v -> formula -> formula
(∀) = for_all
(∃) :: FirstOrderFormula formula atom v => v -> formula -> formula
(∃) = exists
-- | Helper function for building folds.
quant :: FirstOrderFormula formula atom v =>
Quant -> v -> formula -> formula
quant Forall v f = for_all v f
quant Exists v f = exists v f
-- |Legacy version of quant from when we supported lists of quantified
-- variables. It also has the virtue of eliding quantifications with
-- empty variable lists (by calling for_all' and exists'.)
quant' :: FirstOrderFormula formula atom v =>
Quant -> [v] -> formula -> formula
quant' Forall = for_all'
quant' Exists = exists'
convertFOF :: (FirstOrderFormula formula1 atom1 v1, FirstOrderFormula formula2 atom2 v2) =>
(atom1 -> atom2) -> (v1 -> v2) -> formula1 -> formula2
convertFOF convertA convertV formula =
foldFirstOrder qu co tf (atomic . convertA) formula
where
convert' = convertFOF convertA convertV
qu x v f = quant x (convertV v) (convert' f)
co (BinOp f1 op f2) = combine (BinOp (convert' f1) op (convert' f2))
co ((:~:) f) = combine ((:~:) (convert' f))
tf = fromBool
-- |Try to convert a first order logic formula to propositional. This
-- will return Nothing if there are any quantifiers, or if it runs
-- into an atom that it is unable to convert.
toPropositional :: forall formula1 atom v formula2 atom2.
(FirstOrderFormula formula1 atom v,
P.PropositionalFormula formula2 atom2) =>
(atom -> atom2) -> formula1 -> formula2
toPropositional convertAtom formula =
foldFirstOrder qu co tf at formula
where
convert' = toPropositional convertAtom
qu _ _ _ = error "toPropositional: invalid argument"
co (BinOp f1 op f2) = combine (BinOp (convert' f1) op (convert' f2))
co ((:~:) f) = combine ((:~:) (convert' f))
tf = fromBool
at = atomic . convertAtom
-- | Display a formula in a format that can be read into the interpreter.
showFirstOrder :: forall formula atom v. (FirstOrderFormula formula atom v, Show v) => (atom -> String) -> formula -> String
showFirstOrder sa formula =
foldFirstOrder qu co tf at formula
where
qu Forall v f = "(for_all " ++ show v ++ " " ++ showFirstOrder sa f ++ ")"
qu Exists v f = "(exists " ++ show v ++ " " ++ showFirstOrder sa f ++ ")"
co (BinOp f1 op f2) = "(" ++ parenForm f1 ++ " " ++ showCombine op ++ " " ++ parenForm f2 ++ ")"
co ((:~:) f) = "((.~.) " ++ showFirstOrder sa f ++ ")"
tf x = if x then "true" else "false"
at :: atom -> String
at = sa
parenForm x = "(" ++ showFirstOrder sa x ++ ")"
showCombine (:<=>:) = ".<=>."
showCombine (:=>:) = ".=>."
showCombine (:&:) = ".&."
showCombine (:|:) = ".|."
prettyFirstOrder :: forall formula atom v. (FirstOrderFormula formula atom v) =>
(Int -> atom -> Doc) -> (v -> Doc) -> Int -> formula -> Doc
prettyFirstOrder pa pv pprec formula =
parensIf (pprec > prec) $
foldFirstOrder
(\ qop v f -> prettyQuant qop <> pv v <> text "." <+> (prettyFirstOrder pa pv prec f))
(\ cm ->
case cm of
(BinOp f1 op f2) ->
case op of
(:=>:) -> (prettyFirstOrder pa pv 2 f1 <+> pretty op <+> prettyFirstOrder pa pv prec f2)
(:<=>:) -> (prettyFirstOrder pa pv 2 f1 <+> pretty op <+> prettyFirstOrder pa pv prec f2)
(:&:) -> (prettyFirstOrder pa pv 3 f1 <+> pretty op <+> prettyFirstOrder pa pv prec f2)
(:|:) -> (prettyFirstOrder pa pv 4 f1 <+> pretty op <+> prettyFirstOrder pa pv prec f2)
((:~:) f) -> text "¬" {-"~"-} <> prettyFirstOrder pa pv prec f)
(text . ifElse "true" "false")
(pa prec)
formula
where
Fixity prec _ = fixityFirstOrder formula
parensIf False = id
parensIf _ = parens . nest 1
prettyQuant Forall = text "∀" -- "!"
prettyQuant Exists = text "∃" -- "?"
fixityFirstOrder :: (HasFixity atom, FirstOrderFormula formula atom v) => formula -> Fixity
fixityFirstOrder formula =
foldFirstOrder qu co tf at formula
where
qu _ _ _ = Fixity 10 InfixN
co ((:~:) _) = Fixity 5 InfixN
co (BinOp _ (:&:) _) = Fixity 4 InfixL
co (BinOp _ (:|:) _) = Fixity 3 InfixL
co (BinOp _ (:=>:) _) = Fixity 2 InfixR
co (BinOp _ (:<=>:) _) = Fixity 1 InfixL
tf _ = Fixity 10 InfixN
at = fixity
-- | Examine the formula to find the list of outermost universally
-- quantified variables, and call a function with that list and the
-- formula after the quantifiers are removed.
withUnivQuants :: FirstOrderFormula formula atom v => ([v] -> formula -> r) -> formula -> r
withUnivQuants fn formula =
doFormula [] formula
where
doFormula vs f =
foldFirstOrder
(doQuant vs)
(\ _ -> fn (reverse vs) f)
(\ _ -> fn (reverse vs) f)
(\ _ -> fn (reverse vs) f)
f
doQuant vs Forall v f = doFormula (v : vs) f
doQuant vs Exists v f = fn (reverse vs) (exists v f)
-- -------------------------------------------------------------------------
-- Apply a function to the atoms, otherwise keeping structure.
-- -------------------------------------------------------------------------
mapAtomsFirstOrder :: forall formula atom v. FirstOrderFormula formula atom v => (atom -> formula) -> formula -> formula
mapAtomsFirstOrder f fm =
foldFirstOrder qu co tf at fm
where
qu op v p = quant op v (mapAtomsFirstOrder f p)
co ((:~:) p) = mapAtomsFirstOrder f p
co (BinOp p op q) = binop (mapAtomsFirstOrder f p) op (mapAtomsFirstOrder f q)
tf flag = fromBool flag
at x = f x
-- | Deprecated - use mapAtoms
onatoms :: forall formula atom v. (FirstOrderFormula formula atom v) => (atom -> formula) -> formula -> formula
onatoms = mapAtomsFirstOrder
-- -------------------------------------------------------------------------
-- Formula analog of list iterator "itlist".
-- -------------------------------------------------------------------------
foldAtomsFirstOrder :: FirstOrderFormula fof atom v => (r -> atom -> r) -> r -> fof -> r
foldAtomsFirstOrder f i fof =
foldFirstOrder qu co (const i) (f i) fof
where
qu _ _ fof' = foldAtomsFirstOrder f i fof'
co ((:~:) fof') = foldAtomsFirstOrder f i fof'
co (BinOp p _ q) = foldAtomsFirstOrder f (foldAtomsFirstOrder f i q) p
-- | Deprecated - use foldAtoms
overatoms :: forall formula atom v r. FirstOrderFormula formula atom v =>
(atom -> r -> r) -> formula -> r -> r
overatoms f fm b = foldAtomsFirstOrder (flip f) b fm
-- -------------------------------------------------------------------------
-- Special case of a union of the results of a function over the atoms.
-- -------------------------------------------------------------------------
atom_union :: forall formula atom v a. (FirstOrderFormula formula atom v, Ord a) =>
(atom -> Set.Set a) -> formula -> Set.Set a
atom_union f fm = overatoms (\ h t -> Set.union (f h) t) fm Set.empty
$(deriveSafeCopy 1 'base ''Quant)
{- This makes bad things happen.
-- | We can use an fof type as a lit, but it must not use some constructs.
instance FirstOrderFormula fof atom v => Literal fof atom v where
foldLiteral neg tf at fm = foldFirstOrder qu co tf at fm
where qu = error "instance Literal FirstOrderFormula"
co ((:~:) x) = neg x
co _ = error "instance Literal FirstOrderFormula"
atomic = Data.Logic.Classes.FirstOrder.atomic
-}
-- |Just like Logic.FirstOrder.convertFOF except it rejects anything
-- with a construct unsupported in a normal logic formula,
-- i.e. quantifiers and formula combinators other than negation.
fromFirstOrder ::
forall formula atom v lit atom2 .
(Formula lit atom2,
Data.Logic.Classes.FirstOrder.FirstOrderFormula formula atom v,
Literal lit atom2) =>
(atom -> atom2) -> formula -> Failing lit
fromFirstOrder ca formula
= Data.Logic.Classes.FirstOrder.foldFirstOrder
(\ _ _ _ -> Failure ["fromFirstOrder"])
co
(Success . fromBool)
(Success . atomic . ca)
formula
where
co :: Combination formula -> Failing lit
co ((:~:) f) = fromFirstOrder ca f >>= return . (.~.)
co _ = Failure ["fromFirstOrder"]
fromLiteral ::
forall lit atom v fof atom2 .
(Literal lit atom,
Data.Logic.Classes.FirstOrder.FirstOrderFormula fof atom2 v) =>
(atom -> atom2) -> lit -> fof
fromLiteral ca lit = foldLiteral (\ p -> (.~.) (fromLiteral ca p)) fromBool (atomic . ca) lit