{-# language GADTs           #-}
{-# language TypeOperators   #-}
{-# language TypeFamilies    #-}
{-# language DataKinds       #-}
{-# language PolyKinds       #-}
{-# language ConstraintKinds #-}
module Data.PolyKinded.Atom where

import Data.Kind
import Data.PolyKinded

data TyVar d k where
  VZ :: TyVar (x -> xs) x
  VS :: TyVar xs k -> TyVar (x -> xs) k

type V0 = 'Var 'VZ
type V1 = 'Var ('VS 'VZ)
type V2 = 'Var ('VS ('VS 'VZ))
type V3 = 'Var ('VS ('VS ('VS 'VZ)))
type V4 = 'Var ('VS ('VS ('VS ('VS 'VZ))))
type V5 = 'Var ('VS ('VS ('VS ('VS ('VS 'VZ)))))
type V6 = 'Var ('VS ('VS ('VS ('VS ('VS ('VS 'VZ))))))
type V7 = 'Var ('VS ('VS ('VS ('VS ('VS ('VS ('VS 'VZ)))))))
type V8 = 'Var ('VS ('VS ('VS ('VS ('VS ('VS ('VS ('VS 'VZ))))))))
type V9 = 'Var ('VS ('VS ('VS ('VS ('VS ('VS ('VS ('VS ('VS 'VZ)))))))))

data Atom d k where
  Var    :: TyVar d k -> Atom d k
  Kon    :: k         -> Atom d k
  (:@:)  :: Atom d (k1 -> k2) -> Atom d k1 -> Atom d k2

type f :$: x = 'Kon f ':@: x
type a :~: b = 'Kon (~) ':@: a ':@: b

type family Ty (t :: Atom d k) (tys :: LoT d) :: k where
  Ty ('Var 'VZ)     (t ':&&: ts) = t
  Ty ('Var ('VS v)) (t ':&&: ts) = Ty ('Var v) ts
  Ty ('Kon t)       tys          = t
  Ty (f ':@: x)     tys          = (Ty f tys) (Ty x tys)

type family Satisfies (cs :: [Atom d Constraint]) (tys :: LoT d) :: Constraint where
  Satisfies '[]       tys = ()
  Satisfies (c ': cs) tys = (Ty c tys, Satisfies cs tys)