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

import Data.Proxy (Proxy(..))
import Data.Type.Equality ((:~:)(Refl))
import Prelude hiding (compare)

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

-- * Class 'Sym_Ord'

class Sym_Eq repr => Sym_Ord repr where
	compare :: Ord a => repr a -> repr a -> repr Ordering
	default compare :: (Trans t repr, Ord a)
	                => t repr a -> t repr a -> t repr Ordering
	compare = trans_map2 compare

instance (Sym_Ord r1, Sym_Ord r2) => Sym_Ord (Dup r1 r2) where
	compare (x1 `Dup` x2) (y1 `Dup` y2) =
		compare x1 y1 `Dup`
		compare x2 y2

-- * Type 'Expr_Ord'
-- | Expression.
data Expr_Ord (root:: *)
type instance Root_of_Expr      (Expr_Ord root)      = root
type instance Type_of_Expr      (Expr_Ord root)      = No_Type
type instance Sym_of_Expr       (Expr_Ord root) repr = (Sym_Ord repr)
type instance Error_of_Expr ast (Expr_Ord root)      = No_Error_Expr

ord_from
 :: forall root ty ast hs ret.
 ( ty ~ Type_Root_of_Expr (Expr_Ord root)
 , Type_Root_Lift Type_Bool     (Type_Root_of_Expr root)
 , Type_Root_Lift Type_Ordering (Type_Root_of_Expr root)
 , Type_Eq (Type_Root_of_Expr root)
 , Expr_from ast root
 , Error_Expr_Lift (Error_Expr (Error_of_Type ast ty) ty ast)
                   (Error_of_Expr ast root)
 , Root_of_Expr root ~ root
 , Type_Constraint Ord ty
 ) => ast -> ast
 -> Expr_From ast (Expr_Ord root) hs ret
ord_from ast_x ast_y ex ast ctx k =
	expr_from (Proxy::Proxy root) ast_x ctx $
	 \(ty_x::Type_Root_of_Expr root h_x) (Forall_Repr_with_Context x) ->
	expr_from (Proxy::Proxy root) ast_y ctx $
	 \(ty_y::Type_Root_of_Expr root h_y) (Forall_Repr_with_Context y) ->
	when_type_eq ex ast ty_x ty_y $ \Refl ->
	when_type_constraint ex (Proxy::Proxy Ord) ast ty_x $ \Dict ->
		k type_ordering $ Forall_Repr_with_Context $
		 \c -> x c `compare` y c

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

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