Zulip Chat Archive

Stream: new members

Topic: Some confusion in ⟨⟩ notion and Nonempty α


Shanghe Chen (Mar 02 2024 at 07:19):

Hi! In TPIL §.Axioms and Computation. There is

noncomputable def indefiniteDescription {α : Sort u} (p : α  Prop)
(h : x, p x) : {x // p x} :=
choice <| let x, px := h; x, px

and

theorem inahbited_of_nonempty : Nonempty α  Inhabited α
| h => choice (let a := h; ⟨⟨a⟩⟩)

Why does the first snippet use only one ⟨⟩ notation but the second requires ⟨⟨⟩⟩ ?
I think in the first that ⟨⟩ is used for constructor of the subtype {x // p x}:

variable (p : α  Prop)
variable (x : α)
variable (h : p x)
def y : {x // p x} := x, h

But Nonempty not require ⟨⟩? I checked that

noncomputable def indefiniteDescription {α : Sort u} (p : α  Prop)
                                        (h : x, p x) : {x // p x} :=
  choice <| let x, px := h; ⟨⟨x, px⟩⟩

also works.

Shanghe Chen (Mar 02 2024 at 07:25):

Is there some implicit coersion define for Nonemtpy α but not Inhabited α?

Shanghe Chen (Mar 02 2024 at 07:34):

the definition for Nonemtpy α and Inhabited α is

class inductive Nonempty (α : Sort u) : Prop where
|intro (val : α) : Nonempty α

class Inhabited (α: Sort u) where
    default : α

May be some difference of the syntax related to this?

Mario Carneiro (Mar 02 2024 at 08:12):

In the second example we have a : A and are proving Nonempty (Inhabited A)

Mario Carneiro (Mar 02 2024 at 08:13):

This is because after applying choice the goal went from Inhabited A to Nonempty (Inhabited A)

Mario Carneiro (Mar 02 2024 at 08:13):

so we need to apply two constructors in a row

Mario Carneiro (Mar 02 2024 at 08:15):

The proof could alternatively be written as

theorem inhabited_of_nonempty : Nonempty α  Inhabited α
| h => choice (let a := h; a⟩)⟩

which is to say, unwrap the Inhabited before applying choice so that the goal is Nonempty A instead... but at that point the let is just unwrapping Nonempty and wrapping it back up so you can simplify it to just

theorem inhabited_of_nonempty : Nonempty α  Inhabited α
| h => choice h

Shanghe Chen (Mar 02 2024 at 08:21):

Thank you for the alternative proof. It's much more cleaner. I got that there should be two constructor. I am wondering why def z works in the the following snippet where one constructor is omit

variable (p : α  Prop)
variable (x : α)
variable (h : p x)
def y : {x // p x} := x, h
def u : Nonempty {x // p x} := ⟨⟨x, h⟩⟩
def z : Nonempty {x // p x} := x, h -- work too, but why one ⟨⟩ omit?
def v : Nonempty (Inhabited α) := ⟨⟨x⟩⟩
-- def w : Nonempty (Inhabited α) := ⟨x⟩ -- not work

Shanghe Chen (Mar 02 2024 at 08:23):

Oh

def s : Inhabited {x // p x} := x, h
def t : Inhabited {x // p x} := ⟨⟨x, h⟩⟩

both work too. It should relate to soemthing about the subtype

Mario Carneiro (Mar 02 2024 at 08:23):

Oh but actually there is some additional magic which my post doesn't explain, in the first snippet. By similar reasoning we would expect that ⟨x, px⟩ is being used to construct Nonempty {x // p x} and hence we would need double brackets, and indeed ⟨⟨x, px⟩⟩ works. But the magic is that when you use the anonymous constructor brackets with n arguments when the constructor only has m < n, it uses the first m-1 of them for the constructor directly and recursively uses the remaining n-(m-1) as another anonymous constructor for the last argument. This is particularly useful when using ⟨ha, hb, hc, hd⟩ as shorthand syntax for ⟨ha, ⟨hb, ⟨hc, hd⟩⟩⟩ when inhabiting a ∧ b ∧ c ∧ d (which is actually a ∧ (b ∧ (c ∧ d)))

Mario Carneiro (Mar 02 2024 at 08:25):

In this (kind of degenerate) case we get that since ⟨x, px⟩ has 2 arguments and Nonempty expects 1, we use the first 0 of them for Nonempty, recurse with ⟨x, px⟩ to construct a {x // p x} and then pass the result as the last argument to Nonempty, i.e. we get ⟨⟨x, px⟩⟩

Shanghe Chen (Mar 02 2024 at 08:26):

Yeah very clean now. Thank you Mario :tada:


Last updated: May 02 2025 at 03:31 UTC