lam :: ((g->(g,a)) -> (g->(g',b))) -> (g->(a->g',a->b))
lam = (\ f -> \ g -> (\x -> fst $ (f (\ gv->(gv,x))) g,
\y -> snd $ (f (\ gv->(gv,y))) g ))
app :: (g->(a->g1,a->b)) -> (g->(g,a)) -> (g->(g1,b))
app = (\ eA eB -> \ gz -> let (gB, fB) = eB gz
in let (lgA, lfA) = eA gB
in (lgA fB, lfA fB) )
ext :: ((g1,a)->(g2,b))->(g0->(g1,a))->(g0->(g2,b))
ext = \f -> \a -> \g -> f (a g)
idCounter :: Num g => (g,a) -> (g, a)
idCounter = \(g, x) -> (g+1, x)
-- Example terms
trmA = lam (\x -> lam (\y -> app x y))
trmB = (lam (\y -> app trmC y))
trmC = (\g -> (\c->g,\c->c+3))
trmD = (\g -> (g,3))
trmE = app trmC trmD
trmF = (ext idCounter) trmD
trmG = app trmC (app trmC trmF)
trmH = app trmC trmE
Unfortunately, I don't know of a way to tell the compiler that essentially the variables x and y in the lam function will always be applied to the same value. Unfortunately, I've reached a limit of my understanding of how pairs integrate with the type system in Haskell,
I've considered returning something other than a pair from my base function (such as a function) but I have yet to figure that out.