Zulip Chat Archive
Stream: new members
Topic: Handling impossible cases in definitions
Dan Plyukhin (Jan 27 2025 at 18:19):
I often write definitions where certain cases are impossible because of a hypothesis. What's the idiomatic way to write it? Here's an example:
-- You can write partial functions by depending on the proof of a proposition.
def optionify {α β : Type} (f : α → Option β) (h : ∀ a, ∃ b, f a = some b) : α → β :=
fun a =>
match f a, h a with
| some b, _ => b
| none, foo =>
have: False := by cases foo; contradiction
this.elim
The none
branch is a little annoying because I have to explicitly construct False and use the eliminator.
Damiano Testa (Jan 27 2025 at 18:23):
by simp_all
seems to solve it for you.
Dan Plyukhin (Jan 27 2025 at 18:27):
Oh cool! But I have an issue for more complicated cases. I would think I can write something like this:
| none, foo => by cases foo; contradiction
But I get a sorting error on cases foo
:
type mismatch when assigning motive
fun t => foo = t → β
has type
(∃ b, none = some b) → Type : Type 1
but is expected to have type
(∃ b, none = some b) → Prop : Type
Why does cases foo
get stuck where simp_all
succeeds?
Aaron Liu (Jan 27 2025 at 20:37):
This seems to be a bug
Mitchell Lee (Jan 27 2025 at 22:15):
This is not a bug. It is happening because the cases
tactic uses docs#Exists.elim, which only applies if the motive (called b
in the documentation) is a proposition. See here for more details: #new members > Applying tactic `cases` on a non-variable @ 💬
So, the cases
tactic only works inside a proof (i.e. when constructing a term whose type is a proposition). In general, it is not recommended to use tactics for anything except proofs. With this in mind, here is one way you can write your function:
-- You can write partial functions by depending on the proof of a proposition.
def optionify {α β : Type} (f : α → Option β) (h : ∀ a, ∃ b, f a = some b) : α → β :=
fun a =>
match f a, h a with
| some b, _ => b
| none, foo => False.elim (by cases foo; contradiction)
Aaron Liu (Jan 27 2025 at 22:16):
I see. I remember the error message for that case being a little different so I got confused.
Mitchell Lee (Jan 27 2025 at 22:20):
Alternatively, there is a tactic exfalso
that changes any goal to False
. Since this is a proposition, you can then use the cases
tactic afterwards.
-- You can write partial functions by depending on the proof of a proposition.
def optionify {α β : Type} (f : α → Option β) (h : ∀ a, ∃ b, f a = some b) : α → β :=
fun a =>
match f a, h a with
| some b, _ => b
| none, foo => by exfalso; cases foo; contradiction
Dan Plyukhin (Jan 27 2025 at 22:22):
Nice! But if you don't recommend using tactics outside a proof, how would you usually write a function like optionify
? i.e. a function where some cases are impossible, but it's not immediately apparent to the type checker that they're impossible
Mitchell Lee (Jan 27 2025 at 22:26):
I think I most prefer False.elim (by cases foo; contradiction)
. Here, by cases foo; contradiction
is a proof of the proposition False
, so this isn't an example of using tactics outside a proof.
Mitchell Lee (Jan 27 2025 at 22:33):
The way that uses the exfalso
tactic is also acceptable and completely equivalent, though. You should use that one if you prefer it.
It's not recommended to use tactics outside a proof because of issues like the one you ran into with the cases
tactic, and because it can be hard to reason about definitions that were constructed using tactics. In this case, though, it isn't a problem to use the exfalso
tactic as long as you understand what's going on.
Kyle Miller (Jan 27 2025 at 22:35):
Here's an obscure way to do cases:
def optionify {α β : Type} (f : α → Option β) (h : ∀ a, ∃ b, f a = some b) : α → β :=
fun a =>
match f a, h a with
| some b, _ => b
| none, foo => False.elim <| nomatch foo
(The match
itself could theoretically handle this itself, but it's this large elimination issue that's been mentioned (e.g., not being able to do cases
on Exists
when the goal is a type) that's preventing it. Using False.elim
lets match
do its work, and nomatch
is syntax for a zero-alternative match
.)
Last updated: May 02 2025 at 03:31 UTC