{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE PolyKinds #-}
module Language.Symantic.Typing.Family where

import Control.Applicative ((<|>))
import Data.Proxy
import Data.Text (Text)

import Language.Symantic.Typing.Type

-- * Type family 'TyFam'
-- | Apply the /type family/ selected by @fam@
-- to a list of types (within a 'Proxy').
type family TyFam fam (hs::[*]) :: *

-- * Type 'TyFamName'
type TyFamName = Text

-- * Type 'Proj_TyFam'
-- | Convenient type synonym wrapping 'Proj_TyFamR'
-- to initiate its recursion.
type Proj_TyFam cs = Proj_TyFamR cs cs

-- | Project the 'Constraint' indexed by the given 'Type'
-- onto its proof, captured by 'TyCon' when it holds.
proj_TyFam
 :: forall fam cs hs. Proj_TyFam cs fam
 => fam -> Types cs hs -> Maybe (Type cs (TyFam fam hs))
proj_TyFam = proj_TyFamR (Proxy @cs)

-- ** Class 'Proj_TyFamR'
-- | Intermediate type class to construct an instance of 'Proj_TyFam'
-- from many instances of 'Proj_TyFamC', one for each item of @cs@.
--
-- * @cs@: starting  list of /type constants/.
-- * @rs@: remaining list of /type constants/.
class Proj_TyFamR (cs::[*]) (rs::[*]) fam where
	proj_TyFamR
	 :: Proxy rs -> fam -> Types cs hs
	 -> Maybe (Type cs (TyFam fam hs))
	proj_TyFamR _rs _fam _ty = Nothing

-- | Test whether @c@ handles the work of 'Proj_TyFam' or not,
-- or recurse on @rs@, preserving the starting list of /type constants/.
instance
 ( Proj_TyFamC cs    fam c
 , Proj_TyFamR cs rs fam
 ) => Proj_TyFamR cs (Proxy c ': rs) fam where
	proj_TyFamR _rs fam typ =
		proj_TyFamC (Proxy @c)  fam typ <|>
		proj_TyFamR (Proxy @rs) fam typ
-- | End the recursion.
instance Proj_TyFamR cs '[] fam

-- ** Class 'Proj_TyFamC'
-- | Handle the work of 'Proj_TyFam' for a given /type family/ @fam@ and /type constant/ @c@,
-- that is: maybe it handles the given /type family/,
-- and if so, maybe the /type family/ has an instance for the given 'Type'.
class Proj_TyFamC cs fam (c::kc) where
	proj_TyFamC
	 :: Proxy c -> fam -> Types cs hs
	 -> Maybe (Type cs (TyFam fam hs))
	proj_TyFamC _c _fam _ty = Nothing