{-# language ConstraintKinds       #-}
{-# language FlexibleContexts      #-}
{-# language FlexibleInstances     #-}
{-# language GADTs                 #-}
{-# language MultiParamTypeClasses #-}
{-# language ScopedTypeVariables   #-}
{-# language TypeFamilies          #-}
{-# language TypeInType            #-}
{-# language TypeOperators         #-}
{-# language UndecidableInstances  #-}
module Data.PolyKinded (
  
  LoT(..), (:@@:), LoT1, LoT2
, HeadLoT, TailLoT, SpineLoT
  
, SLoT(..), SForLoT(..), Proxy(..)
  
, SplitF, Nat(..), TyEnv(..), SplitN
) where
import           Data.Kind
import           Data.Proxy
infixr 5 :&&:
data LoT k where
  
  LoT0    ::                LoT Type
  
  (:&&:)  :: k -> LoT ks -> LoT (k -> ks)
type LoT1 a = a ':&&: 'LoT0
type LoT2 a b = a ':&&: b ':&&: 'LoT0
type family (f :: k) :@@: (tys :: LoT k) :: Type where
  f :@@: _  = f
  f :@@: as = f (HeadLoT as) :@@: TailLoT as
type family HeadLoT (tys :: LoT (k -> k')) :: k where
  HeadLoT (a ':&&: _) = a
type family TailLoT (tys :: LoT (k -> k')) :: LoT k' where
  TailLoT (_ ':&&: as) = as
type family SpineLoT (ts :: LoT k) :: LoT k where
  SpineLoT (ts :: LoT (k -> k')) = HeadLoT ts ':&&: SpineLoT (TailLoT ts)
  SpineLoT (ts :: LoT Type)      = 'LoT0
data SLoT (l :: LoT k) where
  SLoT0 :: SLoT 'LoT0
  SLoTA :: Proxy t -> SLoT ts -> SLoT (t ':&&: ts)
class SForLoT (l :: LoT k) where
  slot :: SLoT l
instance (l ~ 'LoT0) => SForLoT (l :: LoT Type) where
  slot :: SLoT l
slot = SLoT l
SLoT 'LoT0
SLoT0
instance (l ~ (t ':&&: ts), SForLoT ts) => SForLoT (l :: LoT (k -> k')) where
  slot :: SLoT l
slot = Proxy t -> SLoT ts -> SLoT (t ':&&: ts)
forall k ks (t :: k) (ts :: LoT ks).
Proxy t -> SLoT ts -> SLoT (t ':&&: ts)
SLoTA Proxy t
forall k (t :: k). Proxy t
Proxy SLoT ts
forall k (l :: LoT k). SForLoT l => SLoT l
slot
type SplitF (t :: d) (f :: k) = SplitF' t f 'LoT0
type family SplitF' (t :: d) (f :: k) (p :: LoT l) :: LoT k where
  SplitF' f     f acc = acc
  SplitF' (t a) f acc = SplitF' t f (a ':&&: acc)
data Nat = Z | S Nat
data TyEnv where
  TyEnv :: forall k. k -> LoT k -> TyEnv
type family SplitN (n :: Nat) t :: TyEnv where
  SplitN n t = SplitN' n t 'LoT0
type family SplitN' (n :: Nat) (t :: d) (p :: LoT d) :: TyEnv where
  SplitN' 'Z     t            acc = 'TyEnv t acc
  SplitN' ('S n) (t (a :: l)) acc = SplitN' n t (a ':&&: acc)