Zulip Chat Archive

Stream: new members

Topic: named match constructs


Henning Dieterichs (Nov 30 2020 at 15:06):

These _match_{n} names really start to annoy me (I have quite a few of them).
Here is a very simple example:

def test (p: nat × nat): nat × nat := match p with
| (a, b) := (a, b)
end

lemma baz (p: (nat × nat)): test p = p :=
begin
    rw test,
    cases p,
    rw test._match_1,
end

Can I somehow name them without introducing another function?

Rob Lewis (Nov 30 2020 at 15:43):

You should never really need to deal with these manually. Do the cases first and use simp instead of rw to unfold test.

lemma baz (p: (nat × nat)) (h : test p = p) : false :=
begin
  cases p,
  simp only [test] at h,
end

Mario Carneiro (Nov 30 2020 at 15:55):

You should also try to use the equation compiler instead of match because it gets better names:

def test : nat × nat -> nat × nat
| (a, b) := (a, b)

Eric Wieser (Nov 30 2020 at 15:56):

Is there any way to use the equation compiler in a lambda? Or is λ x, match x with ... | ... end unavoidable?

Henning Dieterichs (Nov 30 2020 at 15:58):

In my case, both things don't work always.
I have definitions like this:

def ant_eval' : Ant (bool)  option Result
| (Ant.leaf matches leaf) := if matches
    then some $ Result.leaf leaf
    else some $ Result.no_match
| (Ant.branch tr1 tr2) :=  match (ant_eval' tr1, ant_eval' tr2) with
    | (some Result.no_match, r) := r
    | (r, some Result.no_match) := r
    | _ := none
    end
| (Ant.diverge matches tr) := match (matches, ant_eval' tr) with
    | (ff, r) := r
    | (tt, some Result.no_match) := some Result.diverged
    | _ := none
    end

I often need to do cases on both arguments of the match, which then leads to 16 cases - that gets very confusing very quickly. I'd like to name the matches, so I can add lemmas that help to deal with them. I already to this, but find the _match_{n} very confusing.

Mario Carneiro (Nov 30 2020 at 16:02):

You don't need to match on a pair, you can just match two things

Mario Carneiro (Nov 30 2020 at 16:08):

But you can always hoist a match statement out of whatever context it is in and make your own definition. Something like this:

def merge_result : option Result  option Result  option Result
| (some Result.no_match) r := r
| r (some Result.no_match) := r
| _ _ := none

def diverge_result : bool  option Result  option Result
| ff r := r
| tt (some Result.no_match) := some Result.diverged
| _ _ := none

def ant_eval' : Ant (bool)  option Result
| (Ant.leaf matches leaf) := if matches
    then some $ Result.leaf leaf
    else some $ Result.no_match
| (Ant.branch tr1 tr2) :=  merge_result (ant_eval' tr1) (ant_eval' tr2)
| (Ant.diverge matches tr) := diverge_result matches (ant_eval' tr)

This is what lean is doing anyway when you use match. But this gives you the opportunity to prove theorems specifically about merge_result and diverge_result too, which means you don't have as many nested cases to deal with

Mario Carneiro (Nov 30 2020 at 16:09):

Given the structure of these matches, it might be better to just use cases on r = some Result.no_match instead of a pattern match

Mario Carneiro (Nov 30 2020 at 16:10):

that will cut your 16 cases down to 3

Henning Dieterichs (Nov 30 2020 at 16:11):

I understand, and probably this is what I should do. However, I don't like that you cannot see by simply looking at ant_eval' that it resembles the algorithm of the paper that I want to prove correct, as many interesting details are scattered into smaller functions.

Henning Dieterichs (Nov 30 2020 at 16:12):

use cases on r = some Result.no_match instead of a pattern match

I though lean cannot unfold the definition if I just have r != some Result.no_match? I guess I need assisting lemmas to make this more comfortable to use?

Mario Carneiro (Nov 30 2020 at 16:22):

If you have r != some Result.no_match and you wrote if r = some Result.no_match then .. else .. in the definition then lean will be able to rewrite it away using split_ifs

Henning Dieterichs (Nov 30 2020 at 16:24):

So do you suggest to replace the matchs in ant_eval' with two nested if/elses?

Mario Carneiro (Nov 30 2020 at 16:24):

yes

Mario Carneiro (Nov 30 2020 at 16:25):

although I think merge_result looks like a good side definition anyway

Mario Carneiro (Nov 30 2020 at 16:26):

diverge_result looks like a nested if-then to me

Henning Dieterichs (Nov 30 2020 at 16:26):

and merge_result?

Mario Carneiro (Nov 30 2020 at 16:27):

def merge_result (r₁ r₂ : option Result) : option Result :=
if r₁ = some Result.no_match then r₂ else
if r₂ = some Result.no_match then r₁ else none

Henning Dieterichs (Nov 30 2020 at 16:28):

oh these subscripts look great, I forgot that I can use them, thanks for pointing that out ;) Yeah, you are right, this definition might even be more readable


Last updated: Dec 20 2023 at 11:08 UTC