0
votes

Lets say I have the following

data Expr : Type -> Type where
  Lift : a -> Expr a
  Add  : Num a => Expr a -> Expr a -> Expr a
  Cnst : Expr a -> Expr b -> Expr a

data Context : Type -> Type where
  Root : Context ()
  L    : Expr w -> Context x -> Expr y -> Expr z -> Context w
  R    : Expr w -> Context x -> Expr y -> Expr z -> Context w 
  M    : Expr w -> Context x -> Expr y -> Expr z -> Context w 

data Zipper : Type -> Type -> Type where
  Z : Expr f -> Context g -> Zipper f g
  E : String -> Context g -> Zipper String ()

I would like to write a function to rebuild a Zipper if I'm going up one level in the expression tree. Something like:

total
ZipUp : Zipper focus parent -> Type
ZipUp (Z e (R (Cnst {a} e1 e2) {x} c t u)) = Zipper a x
ZipUp (Z {f} e (L (Cnst l r) {x} c t u))   = Zipper f x
ZipUp (Z e (R (Add {a} e1 e2) {x} c t u))  = Zipper a x
ZipUp (Z {f} e (L (Add e1 e2) {x} c t u))  = Zipper f x
ZipUp _                                    = Zipper String ()

the problem comes when I want to write the function to return a ZipUp

up : (x : Zipper focus parent) -> ZipUp x
up (Z e (R (Cnst l r) c x y)) = Z (Cnst l e) c
up (Z e (L (Cnst l r) c x y)) = Z (Cnst e r) c
up (Z e (R (Add l r) c x y))  = Z (Add l e) c
up (Z e (L (Add l r) c x y))  = Z (Add e r) c
up (Z e (R (Lift x) c l r))   = E "Some error" c
up (Z e (L (Lift x) c l r))   = E "Some error" c
up (E s c)                    = E s c 

This fails to typecheck on the Add case, because it can't infer that focus (type of e) matches with parent (expected type)

So my question has two parts then.

What can I do to express this relationship? (that if the Zipper is an R constructed, e has the same type as r, or that e has the same type as l in the L constructed case. I've tried using {e = l} and other variants of this, but to no avail.)

The the code typechecks and runs if I comment out the last for lines of up to finish with:

up : (x : Zipper focus parent) -> ZipUp x
up (Z e (R (Cnst l r) c x y)) = Z (Cnst l e) c
up (Z e (L (Cnst l r) c x y)) = Z (Cnst e r) c

but the actual manipulation of the types should be the same in the Add case, yet this fails to typecheck, why is that?

Thanks for taking the time to read and/or answer!

1

1 Answers

1
votes

This fails to typecheck on the Add case, because it can't infer that focus (type of e) matches with parent (expected type)

Because this is not always the case, e.g.

*main> :t Z (Lift "a") (R (Add (Lift 1) (Lift 2)) Root (Lift 4) (Lift 8))
Z (Lift "a") (R (Add (Lift 1) (Lift 2)) Root (Lift 4) (Lift 8)) : Zipper String Integer

And Add (Lift 1) (Lift "a") doesn't work because of the Num a constraint.

What can I do to express this relationship?

If you want to express the relationship within up: You have e : Expr f and can say that the same f should be used in the Add case:

up (Z {f} e (R (Add l r {a=f}) c x y)) = Z (Add l e) c
up (Z {f} e (L (Add l r {a=f}) c x y)) = Z (Add e r) c
up (Z e (R (Add l r) c x y)) = ?whattodo_1
up (Z e (L (Add l r) c x y)) = ?whattodo_2

Now you have a non total function because of the cases where a != f. I don't quite what you want to do, so I can't offer an option. :-)

If you want to express the relationship in Zipper you could do (very roughly) something like:

data Context : Bool -> Type -> Type where
  Root : Context False ()
  L    : Expr w -> Context b x -> Expr y -> Expr z -> Context False w
  R    : Expr w -> Context b x -> Expr y -> Expr z -> Context True w 
  M    : Expr w -> Context b x -> Expr y -> Expr z -> Context False w 

data Zipper : Type -> Type -> Type where
  Z : Expr f -> Context False g -> Zipper f g
  ZR : Expr f -> Context True f -> Zipper f f
  E : String -> Context b g -> Zipper String ()

Now you construct a proof when building up a zipper that f = g in a R-Context. Again, I cannot be specific, but I hope it helps in some way.