{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
{-# OPTIONS_GHC -fconstraint-solver-iterations=7 #-}
-- | Symantic for 'Maybe'.
module Language.Symantic.Lib.Maybe where

import Control.Monad
import qualified Data.Function as Fun
import qualified Data.Maybe as Maybe
import Data.Proxy
import Data.Type.Equality ((:~:)(Refl))
import Prelude hiding (maybe)

import Language.Symantic.Parsing
import Language.Symantic.Typing
import Language.Symantic.Compiling
import Language.Symantic.Interpreting
import Language.Symantic.Transforming
import Language.Symantic.Lib.Lambda

-- * Class 'Sym_Maybe_Lam'
class Sym_Maybe term where
	_Nothing :: term (Maybe a)
	_Just    :: term a -> term (Maybe a)
	maybe    :: term b -> term (a -> b) -> term (Maybe a) -> term b
	
	default _Nothing :: Trans t term => t term (Maybe a)
	default _Just    :: Trans t term => t term a -> t term (Maybe a)
	default maybe    :: Trans t term => t term b -> t term (a -> b) -> t term (Maybe a) -> t term b
	
	_Nothing = trans_lift _Nothing
	_Just    = trans_map1 _Just
	maybe    = trans_map3 maybe

type instance Sym_of_Iface (Proxy Maybe) = Sym_Maybe
type instance Consts_of_Iface (Proxy Maybe) = Proxy Maybe ': Consts_imported_by Maybe
type instance Consts_imported_by Maybe =
 [ Proxy Applicative
 , Proxy Eq
 , Proxy Foldable
 , Proxy Functor
 , Proxy Monad
 , Proxy Monoid
 , Proxy Show
 , Proxy Traversable
 ]

instance Sym_Maybe HostI where
	_Nothing = HostI Nothing
	_Just    = liftM Just
	maybe    = liftM3 Maybe.maybe
instance Sym_Maybe TextI where
	_Nothing = textI0 "Nothing"
	_Just    = textI1 "Just"
	maybe    = textI3 "maybe"
instance (Sym_Maybe r1, Sym_Maybe r2) => Sym_Maybe (DupI r1 r2) where
	_Nothing = dupI0 (Proxy @Sym_Maybe) _Nothing
	_Just    = dupI1 (Proxy @Sym_Maybe) _Just
	maybe    = dupI3 (Proxy @Sym_Maybe) maybe

instance
 ( Read_TypeNameR Type_Name cs rs
 , Inj_Const cs Maybe
 ) => Read_TypeNameR Type_Name cs (Proxy Maybe ': rs) where
	read_typenameR _cs (Type_Name "Maybe") k = k (ty @Maybe)
	read_typenameR _rs raw k = read_typenameR (Proxy @rs) raw k
instance Show_Const cs => Show_Const (Proxy Maybe ': cs) where
	show_const ConstZ{} = "Maybe"
	show_const (ConstS c) = show_const c

instance -- Proj_ConC
 ( Proj_Const cs Maybe
 , Proj_Consts cs (Consts_imported_by Maybe)
 , Proj_Con cs
 ) => Proj_ConC cs (Proxy Maybe) where
	proj_conC _ (TyConst q :$ TyConst c)
	 | Just Refl <- eq_skind (kind_of_const c) (SKiType `SKiArrow` SKiType)
	 , Just Refl <- proj_const c (Proxy @Maybe)
	 = case () of
		 _ | Just Refl <- proj_const q (Proxy @Applicative) -> Just Con
		   | Just Refl <- proj_const q (Proxy @Foldable)    -> Just Con
		   | Just Refl <- proj_const q (Proxy @Functor)     -> Just Con
		   | Just Refl <- proj_const q (Proxy @Monad)       -> Just Con
		   | Just Refl <- proj_const q (Proxy @Traversable) -> Just Con
		 _ -> Nothing
	proj_conC _ (t@(TyConst q) :$ (TyConst c :$ a))
	 | Just Refl <- eq_skind (kind_of_const c) (SKiType `SKiArrow` SKiType)
	 , Just Refl <- proj_const c (Proxy @Maybe)
	 = case () of
		 _ | Just Refl <- proj_const q (Proxy @Eq)
		   , Just Con  <- proj_con (t :$ a) -> Just Con
		   | Just Refl <- proj_const q (Proxy @Monoid)
		   , Just Con  <- proj_con (t :$ a) -> Just Con
		   | Just Refl <- proj_const q (Proxy @Show)
		   , Just Con  <- proj_con (t :$ a) -> Just Con
		 _ -> Nothing
	proj_conC _c _q = Nothing
data instance TokenT meta (ts::[*]) (Proxy Maybe)
 = Token_Term_Maybe_Nothing (EToken meta '[Proxy Token_Type])
 | Token_Term_Maybe_Just    (EToken meta ts)
 | Token_Term_Maybe_maybe   (EToken meta ts) (EToken meta ts)
deriving instance (Eq meta, Eq_Token meta ts) => Eq (TokenT meta ts (Proxy Maybe))
deriving instance (Show meta, Show_Token meta ts) => Show (TokenT meta ts (Proxy Maybe))
instance -- CompileI
 ( Read_TypeName Type_Name (Consts_of_Ifaces is)
 , Inj_Const  (Consts_of_Ifaces is) Maybe
 , Inj_Const  (Consts_of_Ifaces is) (->)
 , Compile is
 ) => CompileI is (Proxy Maybe) where
	compileI tok ctx k =
		case tok of
		 Token_Term_Maybe_Nothing tok_ty_a ->
			-- Nothing :: Maybe a
			compile_type tok_ty_a $ \(ty_a::Type (Consts_of_Ifaces is) a) ->
			check_kind
			 (At Nothing SKiType)
			 (At (Just tok_ty_a) $ kind_of ty_a) $ \Refl ->
			k (ty @Maybe :$ ty_a) $ TermO $
				Fun.const _Nothing
		 Token_Term_Maybe_Just tok_a ->
			-- Just :: a -> Maybe a
			compileO tok_a ctx $ \ty_a (TermO a) ->
			k (ty @Maybe :$ ty_a) $ TermO $
			 \c -> _Just (a c)
		 Token_Term_Maybe_maybe tok_b tok_a2b ->
			-- maybe :: b -> (a -> b) -> Maybe a -> b
			compileO tok_b   ctx $ \ty_b   (TermO b) ->
			compileO tok_a2b ctx $ \ty_a2b (TermO a2b) ->
			check_type2 (ty @(->)) (At (Just tok_a2b) ty_a2b) $ \Refl ty_a2b_a ty_a2b_b ->
			check_type
			 (At (Just tok_a2b) ty_a2b_b)
			 (At (Just tok_b) ty_b) $ \Refl ->
			k (ty @Maybe :$ ty_a2b_a ~> ty_b) $ TermO $
			 \c -> lam $ maybe (b c) (a2b c)
instance
 Inj_Token meta ts Maybe =>
 TokenizeT meta ts (Proxy Maybe) where
	tokenizeT _t = mempty
	 { tokenizers_infix = tokenizeTMod []
		 [ (Term_Name "Nothing",) Term_ProTok
			 { term_protok = \meta -> ProTokPi $ \a ->
				ProTok $ inj_etoken meta $ Token_Term_Maybe_Nothing a
			 , term_fixity = infixN5
			 }
		 , tokenize1 "Just" infixN5 Token_Term_Maybe_Just
		 ]
	 }
instance Gram_Term_AtomsT meta ts (Proxy Maybe) g