Zulip Chat Archive

Stream: new members

Topic: Understanding refl


Morten Brodersen (Jul 18 2021 at 08:35):

I am trying to truly understand the fundamentals of proving using type theory. In the following code I have implemented my own equivalence relation and are proving a few things with and without using tactics. That seems to work. However the rec_on function (used in meq.symmetry_2) is a bit magical to me. I can get it to do what I want, by following the types, but it isn't clear to me why it works?

namespace my_eq

inductive meq {a : Type} : a  a  Prop
| refl {x : a} : meq x x

--private def refl := @meq.refl

def meq.symmetry {a : Type} {x y : a} : meq x y  meq y x :=
begin intro h, cases h, apply h end

def meq.symmetry_2 {a : Type} {x y : a} (exy : meq x y) : meq y x :=
  meq.rec_on exy (λ {x : a}, meq.refl)

def meq.transitive {a : Type} {x y z : a} : meq x y  meq y z  meq x z :=
begin intro h1, intro h2, cases h1, cases h2, apply h1 end

def meq.left {a : Type} {x y z : a} (exz : meq x z) (eyz : meq y z) : meq x y :=
  let ezy := meq.symmetry eyz in
    meq.transitive exz ezy

def meq.right {a : Type} {x y z : a} (exy : meq x y) (exz : meq x z) : meq y z :=
  let eyx := meq.symmetry exy in
    meq.transitive eyx exz

end my_eq

Mario Carneiro (Jul 18 2021 at 09:03):

meq is an indexed inductive type which is true only when the two indices are the same. (That is, it's another way to write =.) The eliminator for this type says that any property C x y is true for all x, y such that meq x y if C x x is true for all x. In normal parlance we would call this the substitution property of equality: if x = y and P x is true then P y is as well. So proving symmetry is as simple as "recursion on equality": we know meq x y so we can assume x and y are the same; thus we are proving meq x x and meq.refl works

Morten Brodersen (Jul 18 2021 at 09:51):

Thanks Mario! Much appreciated.

Morten Brodersen (Jul 18 2021 at 10:15):

OK here is how far I get reasoning about meq.rec_on:

Starting with the type signature:

meq.rec_on : meq ?M_3 ?M_4  (Π {x : ?M_1}, ?M_2 x x)  ?M_2 ?M_3 ?M_4

and substituting to match the required end type (meq y x) and arbitrary type A:

meq.rec_on : meq y x  (Π {x : A}, meq x x)  meq y x

we still require (meq y x) to prove (meq y x). Which we obviously don't have?

Morten Brodersen (Jul 18 2021 at 10:22):

(deleted)

Eric Wieser (Jul 18 2021 at 10:50):

You filled in ?M_2 backwards

Eric Wieser (Jul 18 2021 at 10:51):

#check @meq.rec_on is more insightful as it doesn't show the ?s

Mario Carneiro (Jul 18 2021 at 10:55):

Here ?M_2 is being assigned (\lam y x, meq x y). Note that when you plug in y, x to this you get meq x y which is the goal, and when you plug in x, x as in the minor premise you get meq x x which is the thing we can prove by refl

Morten Brodersen (Jul 18 2021 at 10:55):

I don't think so? I tried both manually and using the editor to auto swap. Here is what I substitute:

meq.rec_on :
  Π {a : Type}
    {C : a  a  Sort u_1}
    { ᾰ_1 : a},
    meq  ᾰ_1  (Π {x : a}, C x x)  C  ᾰ_1

C   -> meq
   -> y
ᾰ_1 -> x

meq.rec_on :
  Π {a : Type}
    {meq : a  a  Sort u_1}
    {y x : a},
    meq y x  (Π {x : a}, meq x x)  meq y x

Mario Carneiro (Jul 18 2021 at 11:00):

You don't want to assign C := meq because that doesn't get you anywhere, as you point out

Mario Carneiro (Jul 18 2021 at 11:00):

the trick is to use C := \lam y x, meq x y instead

Mario Carneiro (Jul 18 2021 at 11:01):

because then C y x, which is what you want to show, works out to meq x y which is actually the goal

Mario Carneiro (Jul 18 2021 at 11:02):

lean will work backwards from the goal to figure out what C should be

Morten Brodersen (Jul 18 2021 at 11:02):

OK that makes sense. What confuses me is the (C x x). It doesn't allow you to use two different args? In other words, you can't do (C y x)?

Mario Carneiro (Jul 18 2021 at 11:03):

Note that it's an argument, so being more restrictive there is to your advantage

Mario Carneiro (Jul 18 2021 at 11:03):

it's saying you only have to prove C x x rather than C x y for all x, y

Mario Carneiro (Jul 18 2021 at 11:03):

in particular, if it wasn't then you wouldn't be able to finish the proof by refl

Mario Carneiro (Jul 18 2021 at 11:04):

You can always use additional arguments beyond the listed ones, like say some other variable a, by using a choice for C that contains a free occurrence of a

Mario Carneiro (Jul 18 2021 at 11:05):

you can even choose not to generalize some of the arguments in the goal, for example you could take C := \lam _ _, meq x y instead

Mario Carneiro (Jul 18 2021 at 11:05):

that would be silly though because in that case C x x is meq x y so you won't have made any progress in the proof

Morten Brodersen (Jul 18 2021 at 11:07):

But (C x x) is (\lam y x, meq x y) x x = (\lam x, meq x x) x = (meq x x) and not (meq x y)?

Mario Carneiro (Jul 18 2021 at 11:09):

I mean if you set C to \lam _ _, meq x y, then C x x = (\lam _ _, meq x y) x x = meq x y

Mario Carneiro (Jul 18 2021 at 11:09):

by underscores I mean an ignored argument, i.e. this is a constant function

Morten Brodersen (Jul 18 2021 at 11:10):

Ahhhhh! I get it now. Because x and y is part of the lambda context so it can be accessed in C.

Morten Brodersen (Jul 18 2021 at 11:11):

Sorry that was exactly what you were saying by using _ _ :-) Thanks Mario this has been really helpful! Looking forward to reading your thesis.

Mario Carneiro (Jul 18 2021 at 11:11):

they are both available in the "outer context", and now C is giving you two more arguments to play with. By choosing whether to use them or not you decide how the goal is generalized

Morten Brodersen (Jul 18 2021 at 11:11):

Yep exactly I get it now :-) Excellent!

Mario Carneiro (Jul 18 2021 at 11:11):

by the way, you can see the specific arguments lean used by

set_option pp.all true
#print meq.symmetry_2

Mario Carneiro (Jul 18 2021 at 11:11):

  @my_eq.meq.rec_on.{0} a (λ (_x _x_1 : a), @my_eq.meq a _x_1 _x) x y exy (λ {x : a}, @my_eq.meq.refl a x)

Mario Carneiro (Jul 18 2021 at 11:12):

this part -> (λ (_x _x_1 : a), @my_eq.meq a _x_1 _x) is what I wrote as \lam y x, meq x y earlier

Morten Brodersen (Jul 18 2021 at 11:13):

Yep. I fully understand it now Mario. I write functional compilers for a living but forgot about the lambda environment and mixing up x with x where I shouldn't.


Last updated: Dec 20 2023 at 11:08 UTC