Zulip Chat Archive
Stream: lean4
Topic: Auto eliminate match
Henrik Böving (Mar 22 2022 at 18:16):
I'm currently implementing some zipper datastructure on a tree for my tableaux prover. I got these types:
inductive Tableau (α : Type u) where
| tip (data : α) : Tableau α
| inner (data : α) (children : List (Tableau α)) : Tableau α
deriving Repr, Inhabited, BEq
inductive Path (α : Type u) where
| top : Path α
| node (invLefts : List (Tableau α)) (parentPath : Path α) (parentData : α) (rights : List (Tableau α)) : Path α
deriving Repr, Inhabited, BEq
structure Location (α : Type u) where
focus : Tableau α
parent : Path α
deriving Repr, Inhabited, BEq
and am defining a function goLeft
that should shift the focus from the current node to its left partner like so:
open Location Tableau Position
def goLeft! [Inhabited α] (loc : Location α) : Location α :=
match loc.parent with
| top => panic! "Can't visit left of top"
| node [] up data right => panic! "Can't visit left of first"
| node (l::left) up data right => ⟨l, node left up data (loc.focus::right)⟩
which is basically the exact same function as in the original zipper paper...however I would like to also have a proof based one that doesnt panic just like List.get
and get!
so I wrote:
def goLeft (loc : Location α) (h : loc.parent = node (l::left) up data right) : Location α :=
match loc.parent with
| node (l::left) up data right => sorry
but its requiring me to still fill out more cases here which I will of course be able to just prove False from and eliminate by hand but I was wondering whether there would be a more automated way to do this?
Henrik Böving (Mar 22 2022 at 18:31):
Hm I guess I could also just:
def goLeft (loc : Location α) (h : loc.parent = node (l::left) up data right) : Location α :=
⟨l, node left up data (loc.focus::right)⟩
?
Henrik Böving (Mar 22 2022 at 18:49):
Also I have a slight doubt this is actually the proof I want to get passed? I mean sure it works out like this but is my API actually reasonable/usable like this? My idea was that this proof could be generated if you are matching on loc.parent yourself but maybe that's not actually how this data structure should be used? I'm unsure about that as well
Arthur Paulino (Mar 22 2022 at 19:59):
What is Position
?
Henrik Böving (Mar 22 2022 at 20:04):
Oh sorry, that is supposed to be Path
Henrik Böving (Mar 22 2022 at 20:04):
Corrected!
Arthur Paulino (Mar 22 2022 at 20:23):
May I question your types?
Arthur Paulino (Mar 22 2022 at 20:29):
The reason I'd question your types is because I suspect it's possible to simplify the API
Henrik Böving (Mar 22 2022 at 20:53):
I did remove the tip
part by now yeah
Henrik Böving (Mar 22 2022 at 20:53):
that makes things easier
Henrik Böving (Mar 22 2022 at 20:58):
But sure go ahead
Arthur Paulino (Mar 22 2022 at 21:07):
It's just that I see three lists in your types and it made me think that ensuring cohesion might be a bit painful. The question is: how do you make sure that every Tableau α
in invLefts
and rights
has the same parent?
Henrik Böving (Mar 22 2022 at 21:10):
The nodes in there are just sub trees, the parent is stored seperately and while traversing through the zipper the nodes and parent get updated accordingly so this is not really an issue
Arthur Paulino (Mar 22 2022 at 21:13):
If you don't ensure that structurally, I think your functions will end up needing hypotheses like that h
Henrik Böving (Mar 22 2022 at 21:17):
As long as the user uses the API the structure will just remain consistent i dont really see your point?
Chris B (Mar 22 2022 at 21:27):
I think you're supposed to use dependent pattern matching. This is a little bootleg since you didn't give all the variables, but:
variable
(left right : List <| Tableau α)
(up parent : Path α)
(data : α)
def goLeft (l : Tableau α) (h : parent = node (l::left) up data right) : Nat :=
match parent, h with
| node (l::left) up data right, _ => 0
Henrik Böving (Mar 22 2022 at 21:28):
ahhhhh, I thought it would just understand that automatically
Arthur Paulino (Mar 22 2022 at 21:35):
(I'm learning as I talk, sorry and please let me know if I'm bothering you :sweat_smile:)
I was talking about these two approaches:
def List.ascending [LE α] : List α → Prop
| a :: b :: t => a ≤ b ∧ ascending (b :: t)
| _ => True
structure NonEmptyAscendingList (α : Type) [LE α] where
data : List α
nonEmpty : data ≠ []
ascending : data.ascending
def NonEmptyAscendingList.getLeast {α : Type} [LE α] : NonEmptyAscendingList α → α
| ⟨[], h, _⟩ => by simp at h
| ⟨h :: _, _, _⟩ => h
def getLeast {α : Type} [LE α] (l : List α) (h : l ≠ []) (ha : l.ascending) : α :=
match l with
| [] => by simp at h
| h :: _ => h
NonEmptyAscendingList
has its hypotheses as structure fields so NonEmptyAscendingList.getLeast
can be implemented with less parameters. Whereas getLeast
needs the hypotheses everytime.
(ignore the fact that ha
is not being used in this example, but suppose it was)
Henrik Böving (Mar 22 2022 at 21:39):
This can work out here yes, but for a zipper the situation of the left list being empty or the right list being empty and in fact also the situation of both lists being empty is a valid state so there is no invariants I can require at construction time, whether the structure is valid or not depends on its use, they can only be used in a wrong way, not constructed.
Last updated: Dec 20 2023 at 11:08 UTC