{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
module T18747B where

import Data.Kind
import Data.Type.Equality

type family Sing :: k -> Type

data SomeSing (k :: Type) where
  SomeSing :: Sing (a :: k) -> SomeSing k

type family Promote (k :: Type) :: Type
type family PromoteX (a :: k) :: Promote k

type family Demote (k :: Type) :: Type
type family DemoteX (a :: k) :: Demote k

type SingKindX (a :: k) = (PromoteX (DemoteX a) ~~ a)

class SingKindX k => SingKind k where
  toSing :: Demote k -> SomeSing k

type instance Demote Type = Type
type instance Promote Type = Type
type instance DemoteX (a :: Type) = Demote a
type instance PromoteX (a :: Type) = Promote a

type instance Demote Bool = Bool
type instance Promote Bool = Bool

data Foo (a :: Type) where MkFoo :: Foo Bool

data SFoo :: forall a. Foo a -> Type where
  SMkFoo :: SFoo MkFoo
type instance Sing = SFoo

type instance Demote (Foo a) = Foo (DemoteX a)
type instance Promote (Foo a) = Foo (PromoteX a)

instance SingKindX a => SingKind (Foo a) where
  toSing MkFoo = SomeSing SMkFoo

