{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeInType #-}
{-# LANGUAGE TypeOperators #-}
module Data.Type.Predicate.Logic (
Evident, Impossible
, type Not, decideNot
, type (&&&), decideAnd
, type (|||), decideOr, type (^||), type (||^)
, type (^^^), decideXor
, type (==>), proveImplies, Implies
, type (<==>), Equiv
, compImpl, explosion, atom
, complementation, doubleNegation, tripleNegation, negateTwice
, contrapositive, contrapositive'
, projAndFst, projAndSnd, injOrLeft, injOrRight
) where
import Data.Singletons
import Data.Singletons.Decide
import Data.Singletons.Prelude.Bool (Sing(..))
import Data.Type.Predicate
import Data.Void
data (&&&) :: Predicate k -> Predicate k -> Predicate k
type instance Apply (p &&& q) a = (p @@ a, q @@ a)
infixr 3 &&&
instance (Decidable p, Decidable q) => Decidable (p &&& q) where
decide (x :: Sing a) = decideAnd @p @q @a (decide @p x) (decide @q x)
instance (Provable p, Provable q) => Provable (p &&& q) where
prove x = (prove @p x, prove @q x)
decideAnd
:: forall p q a. ()
=> Decision (p @@ a)
-> Decision (q @@ a)
-> Decision ((p &&& q) @@ a)
decideAnd = \case
Proved p -> mapDecision (p,) snd
Disproved v -> \_ -> Disproved $ \(p, _) -> v p
data (|||) :: Predicate k -> Predicate k -> Predicate k
type instance Apply (p ||| q) a = Either (p @@ a) (q @@ a)
infixr 2 |||
instance (Decidable p, Decidable q) => Decidable (p ||| q) where
decide (x :: Sing a) = decideOr @p @q @a (decide @p x) (decide @q x)
decideOr
:: forall p q a. ()
=> Decision (p @@ a)
-> Decision (q @@ a)
-> Decision ((p ||| q) @@ a)
decideOr = \case
Proved p -> \_ -> Proved $ Left p
Disproved v -> mapDecision Right (either (absurd . v) id)
type p ^|| q = p ||| Not p &&& q
type p ||^ q = p &&& Not q ||| q
type p ^^^ q = (p &&& Not q) ||| (Not p &&& q)
decideXor
:: forall p q a. ()
=> Decision (p @@ a)
-> Decision (q @@ a)
-> Decision ((p ^^^ q) @@ a)
decideXor p q = decideOr @(p &&& Not q) @(Not p &&& q) @a
(decideAnd @p @(Not q) @a p (decideNot @q @a q))
(decideAnd @(Not p) @q @a (decideNot @p @a p) q)
data (==>) :: Predicate k -> Predicate k -> Predicate k
type instance Apply (p ==> q) a = p @@ a -> q @@ a
infixr 1 ==>
instance Decidable (Impossible ==> p) where
instance Provable (Impossible ==> p) where
prove = explosion @p
instance (Decidable (p ==> q), Decidable q) => Decidable (Not q ==> Not p) where
decide x = case decide @(p ==> q) x of
Proved pq -> Proved $ \vq p -> vq (pq p)
Disproved vpq -> case decide @q x of
Proved q -> Disproved $ \_ -> vpq (const q)
Disproved vq -> Disproved $ \vnpnq -> vpq (absurd . vnpnq vq)
instance Provable (p ==> q) => Provable (Not q ==> Not p) where
prove = contrapositive @p @q (prove @(p ==> q))
instance {-# OVERLAPPING #-} Decidable (p &&& q ==> p) where
instance {-# OVERLAPPING #-} Provable (p &&& q ==> p) where
prove = projAndFst @p @q
instance {-# OVERLAPPING #-} Decidable (p &&& q ==> q) where
instance {-# OVERLAPPING #-} Provable (p &&& q ==> q) where
prove = projAndSnd @p @q
instance {-# OVERLAPPING #-} Decidable (p &&& p ==> p) where
instance {-# OVERLAPPING #-} Provable (p &&& p ==> p) where
prove = projAndFst @p @p
instance {-# OVERLAPPING #-} Decidable (p ==> p ||| q)
instance {-# OVERLAPPING #-} Provable (p ==> p ||| q) where
prove = injOrLeft @p @q
instance {-# OVERLAPPING #-} Decidable (q ==> p ||| q)
instance {-# OVERLAPPING #-} Provable (q ==> p ||| q) where
prove = injOrRight @p @q
instance {-# OVERLAPPING #-} Decidable (p ==> p ||| p)
instance {-# OVERLAPPING #-} Provable (p ==> p ||| p) where
prove = injOrLeft @p @p
type Implies p q = Provable (p ==> q)
type Equiv p q = Provable (p <==> q)
proveImplies :: Prove q -> Prove (p ==> q)
proveImplies q x _ = q x
type (p <==> q) = p ==> q &&& q ==> p
infixr 1 <==>
explosion :: Impossible --> p
explosion x v = absurd $ v x
atom :: p --> Evident
atom = const
complementation :: forall p. (p &&& Not p) --> Impossible
complementation _ (p, notP) _ = notP p
instance {-# OVERLAPPING #-} Provable (p &&& Not p ==> Impossible) where
prove = complementation @p
contrapositive
:: (p --> q)
-> (Not q --> Not p)
contrapositive f x vQ p = vQ (f x p)
contrapositive'
:: forall p q. Decidable q
=> (Not q --> Not p)
-> (p --> q)
contrapositive' f x p = elimDisproof (decide @q x) $ \vQ ->
f x vQ p
doubleNegation :: forall p. Decidable p => Not (Not p) --> p
doubleNegation x vvP = elimDisproof (decide @p x) $ \vP ->
vvP vP
tripleNegation :: forall p. Not (Not (Not p)) --> Not p
tripleNegation _ vvvP p = vvvP $ \vP -> vP p
negateTwice :: p --> Not (Not p)
negateTwice _ p vP = vP p
projAndFst :: (p &&& q) --> p
projAndFst _ = fst
projAndSnd :: (p &&& q) --> q
projAndSnd _ = snd
injOrLeft :: forall p q. p --> (p ||| q)
injOrLeft _ = Left
injOrRight :: forall p q. q --> (p ||| q)
injOrRight _ = Right