Zulip Chat Archive
Stream: lean4
Topic: type depending on an `if`
Scott Morrison (Sep 24 2023 at 09:41):
I worry I'm confused about something basic here. Suppose I want to fill in:
example (P : Prop) [Decidable P] {a b : α} {C : α → Sort _} (x : C a) (y : C b) :
C (if P then a else b) := ...
Here is one answer:
import Mathlib.Tactic.SplitIfs
set_option autoImplicit true
def yuck (P : Prop) [Decidable P] {a b : α} {C : α → Sort _} (x : C a) (y : C b) :
C (if P then a else b) := by
split_ifs
· exact x
· exact y
(If you wonder why I named it yuck
, try #print yuck
.)
Alternatively we can:
def yum (P : Prop) [i : Decidable P] {a b : α} {C : α → Sort _} (x : C a) (y : C b) :
C (if P then a else b) :=
match i with
| isTrue _ => x
| isFalse _ => y
But:
- I had to name the
Decidable
instance, which is a bit smelly. - Having to do this feels like it rather spoils the point of
if
syntax.
Scott Morrison (Sep 24 2023 at 09:41):
Is it really not possible to make
def mmmm (P : Prop) [Decidable P] {C : α → Sort _} (x : C a) (y : C b) :
C (if P then a else b) :=
if P then x else y -- application type mismatch
work somehow?
Marcus Rossel (Sep 24 2023 at 09:44):
You can do this:
def mmmm (P : Prop) [Decidable P] {C : α → Sort _} (x : C a) (y : C b) :
C (if P then a else b) :=
if h : P then if_pos h ▸ x else if_neg h ▸ y
Scott Morrison (Sep 24 2023 at 09:45):
But then my term has nasty Eq.rec
s. It's barely better than yuck
.
Scott Morrison (Sep 24 2023 at 09:51):
I think what I am hoping for an answer to mmmm
is redefining the syntax for if
, so that both if
statements are actually match statements. Note that
def mmmm! (P : Prop) [i : Decidable P] {C : α → Sort _} (x : C a) (y : C b) :
C (match i with | isTrue _ => a | isFalse _ => b) :=
match i with | isTrue _ => x | isFalse _ => y
works just fine, so this is plausible.
Scott Morrison (Sep 24 2023 at 09:53):
Alternatively
def mmmm! (P : Prop) [Decidable P] {C : α → Sort _} (x : C a) (y : C b) :
C (match (inferInstance : Decidable P) with | isTrue _ => a | isFalse _ => b) :=
match (inferInstance : Decidable P) with | isTrue _ => x | isFalse _ => y
avoids needing to name the instance, and still works fine.
Scott Morrison (Sep 24 2023 at 09:58):
Or even nicer:
def mmmm! (P : Prop) [Decidable P] {C : α → Sort _} (x : C a) (y : C b) :
C (match decide P with | true => a | false => b) :=
match decide P with | true => x | false => y
Scott Morrison (Sep 24 2023 at 09:59):
Indeed, one can override the syntax:
macro_rules
| `(if $c then $t else $e) => do
let mvar ← Lean.withRef c `(?m)
`(let_mvar% ?m := $c; wait_if_type_mvar% ?m; match decide $mvar with | true => $t | false => $e)
def mmmm!! (P : Prop) [Decidable P] {C : α → Sort _} (x : C a) (y : C b) :
C (if P then a else b) :=
if P then x else y
Scott Morrison (Sep 24 2023 at 09:59):
So the question becomes: why don't do we this already?
Scott Morrison (Sep 24 2023 at 10:11):
It seems much nicer, and the terms are better. But presumably even if there is no obstruction, it is the mother of all refactors to implement.
Scott Morrison (Sep 24 2023 at 10:12):
I suspect changing if
to mean match
rather than casesOn
will also help with reduction of terms, given our recent experiences there.
Scott Morrison (Sep 24 2023 at 11:05):
Two potential problems:
run_cmd do if false then pure 3 else pure 7
now fails withauxiliary declaration cannot be created when declaration name is not available
, but maybe we could just allow that?- I suspect that this breaks the lazy evaluation of branches of the if statement. Can anyone suggest either an easy test for this, and assuming it fails, any ideas for a solution?
Eric Wieser (Sep 24 2023 at 13:04):
I don't see why you called it yuck
:
set_option pp.proofs.withType false in
#print yuck
/--
def yuck.{u_1, u_2} : {α : Sort u_1} →
(P : Prop) → [inst : Decidable P] → {a b : α} → {C : α → Sort u_2} → C a → C b → C (if P then a else b) :=
fun {α} P [Decidable P] {a b} {C} x y => if h : P then Eq.mpr _ x else Eq.mpr _ y
-/
A single Eq.mpr
in each branch doesn't seem too surprising for a dependent if
Kyle Miller (Sep 24 2023 at 17:55):
The match
expressions are hiding some of the complexity since they pretty print so nicely. Just like the Eq
recursor, there's a motive, which is how the match
expression is able to let the type become specialized depending on the discriminant.
set_option autoImplicit true
def mmmm! (P : Prop) [Decidable P] {C : α → Sort _} (x : C a) (y : C b) :
C (match decide P with | true => a | false => b) :=
match decide P with | true => x | false => y
set_option pp.match false
#print mmmm!
/-
def mmmm!.{u_1, u_2} : {α : Sort u_1} → {a b : α} → (P : Prop) → [inst : Decidable P] →
{C : α → Sort u_2} →
C a → C b → C (mmmm!.match_1 (fun x ↦ α) (decide P) (fun _ ↦ a) fun _ ↦ b) :=
fun {α} {a b} P [Decidable P] {C} x y ↦
mmmm!.match_1 (fun x ↦ C (mmmm!.match_1 (fun x ↦ α) x (fun _ ↦ a) fun _ ↦ b))
(decide P)
(fun _ ↦ x)
(fun _ ↦ y)
-/
Kyle Miller (Sep 24 2023 at 18:03):
It's interesting having if
be able to specialize decide P
to different values in the target type, which is what match
enables. I wonder if it would be more useful to be able to specialize P
itself, rather than decide P
.
For example, this doesn't work (nor does using match decide P with | true => x | false => y
), but I might expect being able to use if P then x else y
in the body.
def mmmm! (P : Prop) [i : Decidable P] {C : Prop → Sort _} (x : C True) (y : C False) : C P :=
match i with
| isTrue _ => x
| isFalse _ => y
Kyle Miller (Sep 24 2023 at 18:44):
I was just exploring a bit -- maybe if
could be notation that figures out how to apply fancyIfImpl
. This lets you simultaneously swap out P
for True
/False
, the decidable instance for instDecidableTrue
/instDecidableFalse
, and decide P
for true
/false
.
def fancyIfImpl (motive : (P : Prop) → [Decidable P] → Bool → Sort _)
(P : Prop) [Decidable P]
(ifTrue : Unit → motive True true)
(ifFalse : Unit → motive False false) :
motive P (decide P) :=
match h : decide P with
| true => by convert eq_true_of_decide h ▸ ifTrue (); simpa using h
| false => by convert eq_false_of_decide h ▸ ifFalse (); simpa using h
def test1 (P : Prop) [Decidable P] {C : Prop → Sort _} (x : C True) (y : C False) :
C P :=
fancyIfImpl (fun P _ _ => C P)
P
(fun _ => x)
(fun _ => y)
def test2 (P : Prop) [Decidable P] {C : α → Sort _} (x : C a) (y : C b) :
C (if P then a else b) :=
fancyIfImpl (fun P _ _ => C (if P then a else b))
P
(fun _ => x)
(fun _ => y)
Last updated: Dec 20 2023 at 11:08 UTC