{-# LANGUAGE DataKinds, DeriveDataTypeable, EmptyCase, ExplicitForAll     #-}
{-# LANGUAGE ExplicitNamespaces, FlexibleInstances, GADTs, KindSignatures #-}
{-# LANGUAGE LambdaCase, PolyKinds, StandaloneDeriving, TemplateHaskell   #-}
{-# LANGUAGE TypeOperators                                                #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
-- | Provides type synonyms for logical connectives.
module Proof.Propositional
       ( type (/\), type (\/), Not, exfalso, orIntroL
       , orIntroR, orElim, andIntro, andElimL
       , andElimR, orAssocL, orAssocR
       , andAssocL, andAssocR, IsTrue(..)
       , Empty(..), withEmpty, withEmpty'
       , refute
       , Inhabited (..), withInhabited
       , prove
       ) where
import Proof.Propositional.Empty
import Proof.Propositional.Inhabited
import Proof.Propositional.TH

import Data.Data          (Data)
import Data.Type.Equality ((:~:))
import Data.Typeable      (Typeable)
import Data.Void

type a /\ b = (a, b)
type a \/ b = Either a b
type Not a  = a -> Void

infixr 2 \/
infixr 3 /\

exfalso :: a -> Not a -> b
exfalso a neg = absurd (neg a)

orIntroL :: a -> a \/ b
orIntroL = Left

orIntroR :: b -> a \/ b
orIntroR = Right

orElim :: a \/ b -> (a -> c) -> (b -> c) -> c
orElim aORb aTOc bTOc = either aTOc bTOc aORb

andIntro :: a -> b -> a /\ b
andIntro = (,)

andElimL :: a /\ b -> a
andElimL = fst

andElimR :: a /\ b -> b
andElimR = snd

andAssocL :: a /\ (b /\ c) -> (a /\ b) /\ c
andAssocL (a,(b,c)) = ((a,b), c)

andAssocR :: (a /\ b) /\ c -> a /\ (b /\ c)
andAssocR ((a,b),c) = (a,(b,c))

orAssocL :: a \/ (b \/ c) -> (a \/ b) \/ c
orAssocL (Left a)          = Left (Left a)
orAssocL (Right (Left b))  = Left (Right b)
orAssocL (Right (Right c)) = Right c

orAssocR :: (a \/ b) \/ c -> a \/ (b \/ c)
orAssocR (Left (Left a))  = Left a
orAssocR (Left (Right b)) = Right (Left b)
orAssocR (Right c)        = Right (Right c)


-- | Utility type to convert type-level (@'Bool'@-valued) predicate function
--   into concrete witness data-type.
data IsTrue (b :: Bool) where
  Witness :: IsTrue 'True

deriving instance Show (IsTrue b)
deriving instance Eq   (IsTrue b)
deriving instance Ord  (IsTrue b)
deriving instance Read (IsTrue 'True)
deriving instance Typeable IsTrue
deriving instance Data (IsTrue 'True)

instance {-# OVERLAPPABLE #-} (Inhabited a, Empty b) => Empty (a -> b) where
  eliminate f = eliminate (f trivial)

refute [t| 0 :~: 1 |]
refute [t| () :~: Int |]

prove [t| Bool |]
prove [t| Int |]
prove [t| Integer |]
prove [t| Word |]
prove [t| Double |]
prove [t| Float |]
prove [t| Char |]
prove [t| Ordering |]
prove [t| forall a. [a] |]
prove [t| Rational |]
prove [t| forall a. Maybe a |]
prove [t| forall n. n :~: n |]
prove [t| IsTrue 'True |]

instance Empty (IsTrue 'False) where
  eliminate = \ case {}