Zulip Chat Archive
Stream: lean4
Topic: Decidable with structures
Heather Macbeth (Jul 05 2024 at 23:01):
I have a Prop
structure for which I wrote a Decidable
instance, but this instance doesn't seem to trigger. Is this expected behaviour? If so, how can I make decide
work for my structure? MWE:
structure Nice (n : Nat) : Prop :=
(large : 100 ≤ n)
theorem nice_iff (n : Nat) : Nice n ↔ 100 ≤ n := ⟨Nice.rec id, Nice.mk⟩
instance (n : Nat) : Decidable (Nice n) := by
rw [nice_iff]
infer_instance
example : Nice 102 := by decide -- fails
example : Nice 102 := by rw [nice_iff]; decide -- works
Kyle Miller (Jul 05 2024 at 23:06):
It fails because you used rw
to define it. Here's the error message:
/-
tactic 'decide' failed for proposition
Nice 102
since its 'Decidable' instance reduced to
⋯ ▸ inferInstance
rather than to the 'isTrue' constructor.
-/
What you should be getting from it is "it knows there's a Decidable instance, but something is wrong with it, and it gets stuck trying to evaluate it". The subst operator (the triangle) hints that it's a rewrite that made it get stuck.
Kyle Miller (Jul 05 2024 at 23:07):
This is one of the "right" ways to write the Decidable instance to circumvent this issue:
instance (n : Nat) : Decidable (Nice n) :=
decidable_of_iff _ (nice_iff _).symm
example : Nice 102 := by decide
Kyle Miller (Jul 05 2024 at 23:09):
The key is that decidable_of_iff
is defined using
def decidable_of_decidable_of_iff [Decidable p] (h : p ↔ q) : Decidable q :=
if hp : p then
isTrue (Iff.mp h hp)
else
isFalse fun hq => absurd (Iff.mpr h hq) hp
and it can reduce this (it's an honest if, and each case is just a constructor of Decidable).
Kyle Miller (Jul 05 2024 at 23:11):
Potentially someone could write a fancier decide
that does a better job at diagnosing the issue. Right now, it just hands you what it sees after putting the instance into weak head normal form, which is simultaneously helpful and useless.
Heather Macbeth (Jul 05 2024 at 23:13):
Thanks! It's a bit reassuring to me that I have definitely never encountered this quirk before (can't use ▸
in decide
) -- at least it's not something I should have figured out :)
Heather Macbeth (Jul 05 2024 at 23:14):
By the way, a related question: is there a derive handler which would generate the Decidable (Nice n)
instance here?
Kyle Miller (Jul 05 2024 at 23:15):
Thinking about it now, it could easily detect that ▸
is there (that's Eq.rec
by the way) and tell you more-or-less what I just told you. It could also tell you which definition(s) might be responsible.
Kyle Miller (Jul 05 2024 at 23:15):
Heather Macbeth said:
By the way, a related question: is there a derive handler which would generate the
Decidable (Nice n)
instance here?
I don't believe so, but it would be Nice
Heather Macbeth (Jul 05 2024 at 23:18):
Kyle Miller said:
I don't believe so, but it would be Nice
All the more so in my actual situation, in which I have a structure with five fields, so the nice_iff
lemma is a real pain to write out:
theorem nice_iff (n : Nat) : Nice n ↔ stuff ∧ stuff ∧ stuff ∧ stuff ∧ stuff :=
⟨Nice.rec (fun h1 h2 h3 h4 h5 ↦ ⟨h1, h2, h3, h4, h5⟩),
fun h ↦ Nice.mk h.1 h.2.1 h.2.2.1 h.2.2.2.1 h.2.2.2.2⟩
There's no better way, right?
Kyle Miller (Jul 05 2024 at 23:19):
Mathlib has the @[mk_iff]
attribute:
@[mk_iff]
structure Nice (n : Nat) : Prop :=
(large : 100 ≤ n)
#print nice_iff
/-
theorem nice_iff : ∀ (n : ℕ), Nice n ↔ 100 ≤ n :=
fun n ↦ { mp := fun a ↦ Nice.casesOn a fun large ↦ large, mpr := fun a ↦ { large := a } }
-/
Maybe we could have a mathlib Decidable
deriving handler so that you can do
structure Nice (n : Nat) : Prop :=
(large : 100 ≤ n)
deriving Decidable
that makes use of the mk_iff
machinery paired with decidable_of_iff
.
Heather Macbeth (Jul 05 2024 at 23:21):
Oh, I didn't know about @[mk_iff]
, and it avoids almost all the pain. Thanks!
Heather Macbeth (Jul 05 2024 at 23:22):
(But indeed, the Decidable
deriving handler would be even better.)
Kyle Miller (Jul 06 2024 at 20:41):
lean4#4674 is an experiment to make the decide
tactic more informative.
structure Nice (n : Nat) : Prop :=
(large : 100 ≤ n)
theorem nice_iff (n : Nat) : Nice n ↔ 100 ≤ n := ⟨Nice.rec id, Nice.mk⟩
instance (n : Nat) : Decidable (Nice n) := by
rw [nice_iff]
infer_instance
example : Nice 102 := by decide
/-
tactic 'decide' failed for proposition
Nice 102
since its 'Decidable' instance did not reduce to 'isTrue' or 'isFalse'.
Instead, after unfolding the instance
instDecidableNice
it reduced to
⋯ ▸ inferInstance
Hint: reduction got stuck on '▸' (Eq.rec), which indicates that one of the instances is
defined using tactics such as 'rw' or 'simp'. Use definitions such as 'inferInstanceAs'
or 'decidable_of_decidable_of_iff' to change or otherwise "rewrite" the type.
-/
To help with debugging, you can even hover over the names of unfolded instances to be able to do Go to Definition.
image.png
Last updated: May 02 2025 at 03:31 UTC