-- Using overloaded labels to provide nice syntactic sugar for a
-- term representation using de Bruijn indices

{-# LANGUAGE OverloadedLabels
           , DataKinds
           , FlexibleContexts
           , FlexibleInstances
           , GADTs
           , KindSignatures
           , MultiParamTypeClasses
           , NoMonomorphismRestriction
           , OverlappingInstances
           , ScopedTypeVariables
           , StandaloneDeriving
           , TypeOperators
  #-}

import GHC.OverloadedLabels
import Data.Proxy ( Proxy(..) )
import GHC.TypeLits ( Symbol )

instance x ~ y => IsLabel x (Proxy y) where
  fromLabel _ = Proxy

data Elem (x :: Symbol) g where
  Top :: Elem x (x ': g)
  Pop :: Elem x g -> Elem x (y ': g)
deriving instance Show (Elem x g)


class IsElem x g where
  which :: Elem x g

instance IsElem x (x ': g) where
  which = Top

instance IsElem x g => IsElem x (y ': g) where
  which = Pop which


data Tm g where
  Var :: Elem x g -> Tm g
  App :: Tm g -> Tm g -> Tm g
  Lam :: Tm (x ': g) -> Tm g
deriving instance Show (Tm g)

instance IsElem x g => IsLabel x (Tm g) where
  fromLabel _ = Var (which :: Elem x g)

lam :: Proxy x -> Tm (x ': g) -> Tm g
lam _ = Lam

s = lam #x #x
t = lam #x (lam #y (#x `App` #y))

u :: IsElem "z" g => Tm g
u = #z `App` #z

main = do print s
          print t
          print (u :: Tm '["z"])
