-- This file was inspired from our development of
-- SimpleTags.prg. We replace
-- Tag' and Label' by the builtin Tag and Label type.
-- In this file we explore the notion of substitution.

-- A Shape is a list or record structure in which
-- we use the built in Tag as labels.
-- The built in Tag types are constructed by using
-- the backquote on a variable like `a

data Shape:: *1 where
  E :: Shape
  P :: Tag ~> *0 ~> Shape ~> Shape
    deriving Record(s)

-- A count is a natural number, which is indexed
-- by shapes and types.

data Count:: Shape ~> Tag ~> *0 ~> *0 where
  Zero:: Count (P a t b) a t
  Succ:: Count sh nm ty -> (Count (P a ty2 sh)) nm ty
    deriving Nat(c)

-- A Term is indexed by a Shape which determines what
-- variables are used in the term, and what type those
-- variables have.

data Term:: Shape ~> *0 ~> *0 where
  Iconst :: Int -> Term sh Int
  Var:: Label a -> Count i a ty -> Term i ty
  Pair:: Term sh a -> Term sh b -> Term sh (a,b)
  Let:: Label a -> Term sh x -> Term (P a x sh) s -> Term sh s
     -- let a = 3 in (a,5)

-- Here are a few examples.
t1 = Let `x (Iconst 3) (Pair (Var `x 0c) (Iconst 5))
t2 = Let `x (Iconst 3) (Pair (Var `x 0c) (Var `y 1c))

-- First a quick side trip. What if we want to add Succ
-- to every variable in a term?  Term sh t -> Term (P c x sh) t

data Bumper:: Shape ~> Shape ~> *0 where
  Base:: Bumper sh (P c t sh)
  Step:: Bumper sh1 sh2 -> Bumper (P c t sh1) (P c t sh2)
 deriving Nat(b)

bumpV:: Bumper sh1 sh2 -> Count sh1 c t -> Count sh2 c t
bumpV Base v = Succ v
bumpV (Step m) Zero = Zero
bumpV (Step m) (Succ n) = Succ(bumpV m n)

bump:: (Bumper sh1 sh2) -> Term sh1 x -> Term sh2 x
bump f (Var x v) = Var x (bumpV f v)
bump f (Iconst n) = Iconst n
bump f (Pair x y) = Pair (bump f x) (bump f y)
bump f (Let v e1 e2) = Let v (bump f e1) (bump (Step f) e2)

add1:: Term sh t -> Term (P c x sh) t
add1 term = bump Base term

-- Substitution will replace the variable outer most
-- in the shape with a term of the same type.
-- The term being substituted into will have type: Term (P a x sh) s
-- The variable is 'a' and its type is 'x'
-- The term being substituted for 'a' will have type: Term sh x
-- Here is our first try.

subst:: Term sh x -> Term (P a x sh) s -> Term sh s
subst t (Var x 0c) = t
subst t (Var x (Succ c)) = Var x c
subst t (Iconst c) = Iconst c
subst t (Pair x y) = Pair (subst t x) (subst t y)
-- subst t (Let v e1 e2) = check Let v (subst t e1) (subst t e2)

-- The problem here is that the term 'e2' doesn't have the right shape.
-- In fact, subst replaces the variable with count Zero, but inside
-- 'e2' the new variable 'v' has Count Zero, so inside 'e2' we
-- want 't' to replace the variable with Count (Succ Zero)
-- If you remove the comment in the last line you can poke around
-- inside the 'check' and see for yourself that the shape isn't correct.

-- We need to count how many 'Let's we have walked under. And we
-- need to adjust the shape as we do it. Think of a Substitution as
-- a indicating both the starting and ending shape, and which encloses
-- the term to substitute for the variable.

data Substitution :: Shape ~> Shape ~> *0 ~> *0 where
  Top:: Term s t -> Substitution (P c t s) s t
  Next:: Substitution sh1 sh2 t2 -> Substitution (P c t3 sh1) (P c t3 sh2) t2

sub1 :: Substitution {a=Int; b}s b Int
sub1 = Top (Iconst 1)

sub2:: Substitution {a=b,c=Int; d}s {a=b; d}s Int
sub2 = Next sub1

subst2:: (Substitution sh1 sh2 t1) -> Term sh1 t2 -> Term sh2 t2
subst2 (Top t)  (Var x Zero) = t
subst2 (Next _) (Var x Zero) = Var x Zero
subst2 (Top t)  (Var x (Succ c)) = Var x c
subst2 (Next t) (Var x (Succ c)) = bump Base (subst2 t (Var x c))
subst2 t (Iconst c) = Iconst c
subst2 t (Pair x y) = Pair (subst2 t x) (subst2 t y)
subst2 t (x@(Let v e1 e2)) = Let v (subst2 t e1) (subst2 (Next t) e2)

-- One way to get around this is to add a new Constructor 'Bump' to terms
-- Encapsulating the bump operator

data TermB:: Shape ~> *0 ~> *0 where
  Iconst2 :: Int -> TermB sh Int
  Var2:: Label a -> Count i a ty -> TermB i ty
  Pair2:: TermB sh a -> TermB sh b -> TermB sh (a,b)
  Let2:: Label a -> TermB sh x -> TermB (P a x sh) s -> TermB sh s
  Bump:: TermB sh x -> TermB (P a t sh) x

data SubstitutionB :: Shape ~> Shape ~> *0 ~> *0 where
  TopB:: TermB s t -> SubstitutionB (P c t s) s t
  NextB:: SubstitutionB sh1 sh2 t2 -> SubstitutionB (P c t3 sh1) (P c t3 sh2) t2

substB:: (SubstitutionB sh1 sh2 t1) -> TermB sh1 t2 -> TermB sh2 t2
substB (TopB t)  (Var2 x Zero) = t
substB (NextB _) (Var2 x Zero) = Var2 x Zero
substB (TopB t)  (Var2 x (Succ c)) = Var2 x c
substB (NextB t) (Var2 x (Succ c)) = Bump (substB t (Var2 x c))
substB t (Iconst2 c) = Iconst2 c
substB t (Pair2 x y) = Pair2 (substB t x) (substB t y)
-- substB (TopB t) (Bump x) = check undefined
substB t (x@(Let2 v e1 e2)) = Let2 v (substB t e1) (substB (NextB t) e2)