Description
Currently Lean's pattern matching doesn't have ML-style or-patterns leading to somewhat repetitive of this kind:
def balanceLB {h c} : almostNode h → Int → rbnode h c → hiddenTree (h + 1)
| LR (R a x b) y c, z, d => HR (R (B a x b) y (B c z d))
| RR a x (R b y c), z, d => HR (R (B a x b) y (B c z d))
| V axb, z, d => HB (B axb z d)
You'll notice that the RHS of the first two cases are exactly identical. I propose, we support ML-style or-patterns to allow more compact code like this:
def balanceLB {h c} : almostNode h → Int → rbnode h c → hiddenTree (h + 1)
| LR (R a x b) y c, z, d
| RR a x (R b y c), z, d => HR (R (B a x b) y (B c z d))
| V axb, z, d => HB (B axb z d)
The following is a proposed spec for the feature:
Currently, the Lean parser always expects a =>
after the LHS of a pattern match. As such there would be no ambiguity adding or-patterns by leaving out the RHS for certain patterns.
I propose, we allow either the =>
or a further pattern after a LHS has been completed, i.e. the correct number of arguments has been matched.
Several patterns are compatible if the following rules hold:
- The set of matched variables is the same. In the sense that the same names are bound, in arbitrary order.
- Bound constants do not count as variables and can be different between patterns
- wildcard patterns do not count as variables and can be different between patterns
- Each variable that occurs in shared patterns has to have the same type. Here "same" means definitionally equal.
This will allow us to write the following:
def fib : Nat → Nat
| 0 => 1
| 1 => 1
| (n+2) => fib n + fib (n+1)
def fib2 (n : Nat) : Nat :=
match n with
| 0 => 1
| 1 => 1
| (n+2) => fib2 n + fib2 (n+1)
on the other hand the following is forbidden:
inductive strOrNum
| S (s : String)
| I (i : Int)
open strOrNum
def ex1 :=
match S "test" with
| I a | S a => 3
It is forbidden, because the types of the variables a
are not definitionally equal. On the other hand the following is allowed:
def ex1 :=
match S "test" with
| I _ | S _ => 3
Semantically, the left out pattern takes on exactly the term that's the RHS of the last merged pattern:
def fib : Nat → Nat
| 0
| 1 => 1
| (n+2) => fib n + fib (n+1)
is the same as:
def fib : Nat → Nat
| 0 => 1
| 1 => 1
| (n+2) => fib n + fib (n+1)
For pretty-printing purposes, I propose sticking to whatever was written in the source code, i.e. merge the patterns that are merged in source code and keep the ones separate that were separate.
For proof and computational purposes, the two syntaxes should be interchangeable:
(match n with
| 0 | 1 => 1
| (n+2) => fib n + fib (n+1)) =
(match n with
| 0 => 1
| 1 => 1
| (n+2) => fib n + fib (n+1))
as such the aforementioned equality should ideally hold definitionally. This could be achieved by the former reducing to the latter.
For equation lemmas that we are going to have anyway for the match expressions, the or-patterns can have additional lemmas:
Whilst the normal match expression has lemmas for each case asserting the previous cases weren't taken. The or-patterns have theses premises combined in a disjunction.
This new equation lemma is potentially useful in proofs where one might want to treat the RHS uniformly.
I feel fairly strongly about this one, since it may well allow us to do a bunch of operations on an RHS without having to do a cases
followed by applying the same exact chain of tactics many times. Consider this example:
def balance : color → node → Nat → node → node
| color.black, node.tree color.red (node.tree color.red a x b) y c, z, d
| color.black, node.tree color.red a x (node.tree color.red b y c), z, d
| color.black, a, x, node.tree color.red (node.tree color.red b y c) z d
| color.black, a, x, node.tree color.red b y (node.tree color.red c z d) =>
node.tree color.red (node.tree color.black a x b) y (node.tree color.black c z d)
| color, a, x, b => node.tree color a x b
if we have some kind of lemma that is useful for massaging the RHS, we can uniformly rewrite the RHS for all the cases in one go and only use the premises later in the proof. At which point, we can possibly weaken the premises and unify them into fewer cases that we have to do actual work about.
Feel free to poke holes at the initial spec, I'm happy to do more homework and extend it as needed.
Activity