{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
-- | Expression for 'Bool'.
module Language.LOL.Symantic.Expr.Bool where

import Data.Proxy (Proxy(..))
import Prelude hiding (and, not, or)

import Language.LOL.Symantic.Type
import Language.LOL.Symantic.Expr.Common
import Language.LOL.Symantic.Expr.Lambda
import Language.LOL.Symantic.Repr.Dup
import Language.LOL.Symantic.Trans.Common

-- * Class 'Sym_Bool'

-- | Symantic.
class Sym_Bool repr where
	bool ::      Bool -> repr Bool
	not  :: repr Bool -> repr Bool
	and  :: repr Bool -> repr Bool -> repr Bool
	or   :: repr Bool -> repr Bool -> repr Bool
	xor  :: repr Bool -> repr Bool -> repr Bool
	xor x y = (x `or` y) `and` not (x `and` y)
	
	default bool :: Trans t repr =>        Bool -> t repr Bool
	default not  :: Trans t repr => t repr Bool -> t repr Bool
	default and  :: Trans t repr => t repr Bool -> t repr Bool -> t repr Bool
	default or   :: Trans t repr => t repr Bool -> t repr Bool -> t repr Bool
	bool = trans_lift . bool
	not  = trans_map1 not
	and  = trans_map2 and
	or   = trans_map2 or

instance -- Sym_Bool Dup
 ( Sym_Bool r1
 , Sym_Bool r2
 ) => Sym_Bool (Dup r1 r2) where
	bool x = bool x `Dup` bool x
	not (x1 `Dup` x2)               = not x1    `Dup` not x2
	and (x1 `Dup` x2) (y1 `Dup` y2) = and x1 y1 `Dup` and x2 y2
	or  (x1 `Dup` x2) (y1 `Dup` y2) = or  x1 y1 `Dup` or  x2 y2
	xor (x1 `Dup` x2) (y1 `Dup` y2) = xor x1 y1 `Dup` xor x2 y2

-- * Type 'Expr_Bool'
-- | Expression.
data Expr_Bool (root:: *)
type instance Root_of_Expr      (Expr_Bool root)      = root
type instance Type_of_Expr      (Expr_Bool root)      = Type_Bool
type instance Sym_of_Expr       (Expr_Bool root) repr = Sym_Bool repr
type instance Error_of_Expr ast (Expr_Bool root)      = No_Error_Expr

-- ** Type 'Expr_Lambda_Bool'
-- | Convenient alias.
type Expr_Lambda_Bool lam = Expr_Root (Expr_Alt (Expr_Lambda lam) Expr_Bool)

-- | Convenient alias around 'expr_from'.
expr_lambda_bool_from
 :: forall lam ast.
 Expr_from ast (Expr_Lambda_Bool lam)
 => Proxy lam
 -> ast
 -> Either (Error_of_Expr ast (Expr_Lambda_Bool lam))
           (Exists_Type_and_Repr (Type_Root_of_Expr (Expr_Lambda_Bool lam))
                                 (Forall_Repr (Expr_Lambda_Bool lam)))
expr_lambda_bool_from _lam ast =
	expr_from (Proxy::Proxy (Expr_Lambda_Bool lam)) ast
	 Context_Empty $ \ty (Forall_Repr_with_Context repr) ->
		Right $ Exists_Type_and_Repr ty $
			Forall_Repr $ repr Context_Empty