Zulip Chat Archive

Stream: Is there code for X?

Topic: Is `Nat.noConfusionType` in Lean 4 Circular Reasoning When P


Lingwei Peng (彭灵伟) (Mar 08 2025 at 05:52):

I noticed that in Lean 4, when proving 0 ≠ n.succ, Nat.noConfusionType is used. It is defined as follows:

def Nat.noConfusionType.{u} : Sort u  Nat  Nat  Sort u :=
  fun P v1 v2 => Nat.casesOn v1 (Nat.casesOn v2 (P  P) fun n => P) fun n => Nat.casesOn v2 P fun n_1 => (n = n_1  P)  P

My understanding is that this expression is equivalent to:

if v1 = 0 then
  if v2 = 0 then P  P else P
else
  if v2 = 0 then P else (n = v2  P)  P

This essentially requires first defining when n = 0 and when n ≠ 0. So, is using Nat.noConfusionType to prove 0 ≠ n.succ a form of circular reasoning?

Kevin Buzzard (Mar 08 2025 at 08:21):

No it's definitely not circular reasoning -- that is not allowed in lean. The axiom of recursion says that you can define a function on natural numbers by saying what to do with zero and what to do with successors, and you're just noticing that this axiom is enough to imply noConfusion.

suhr (Mar 08 2025 at 08:34):

colorlessboy said:

My understanding is that this expression is equivalent to:

if v1 = 0 then
  if v2 = 0 then P  P else P
else
  if v2 = 0 then P else (n = v2  P)  P

This essentially requires first defining when n = 0 and when n ≠ 0. So, is using Nat.noConfusionType to prove 0 ≠ n.succ a form of circular reasoning?

It is not equivalent: casesOn uses the recursor of Nat, for which Lean has a built-in computation rule, while if ... then ... else works in a much more roundabout way: under the hood it's application of the ite function which uses an instance of the Decidable inferred for the given proposition.

suhr (Mar 08 2025 at 08:38):

Here's an example (open it in Lean 4 playground):

def foo : Sort u  Nat  Nat  Sort u :=
  fun P v1 v2 =>
    if v1 = 0 then
      if v2 = 0 then P  P else P
    else
      if v2 = 0 then P else (v1 = v2  P)  P

set_option pp.notation false in
  #print foo

You can click on ite to see the implicit arguments.

Lingwei Peng (彭灵伟) (Mar 08 2025 at 08:39):

Thanks. Do you mean Decidable n = 0?

suhr (Mar 08 2025 at 08:41):

Decidable (v1 = 0), but yeah. By the way, you can find the instance with #synth ∀n: Nat, Decidable (n = 0).

suhr (Mar 08 2025 at 08:48):

By the way, here are computation rules for Nat.rec:

Nat.rec z s (Nat.zero)    z
Nat.rec z s (Nat.succ n)  s n (Nat.rec z s n)

Which also imply that Nat.rec has the following type:

Nat.rec.{u}: {motive: Nat  Sort u} 
  motive Nat.zero 
  ((n: Nat)  motive n  motive n.succ) 
  (t: Nat),  motive t

In the same way one can deduce the computation rules and the type of the recursor for any other inductive type.

suhr (Mar 08 2025 at 08:55):

Works for type families too. For example, computation rules for Nat.le.rec:

Nat.le.rec r s (@Nat.le.refl n)      r
Nat.le.rec r s (@Nat.le.step n m h)  s m h (Nat.le.rec r s h)

And the type of the recursor:

Nat.le.rec: {n: Nat} {motive: m: Nat, n  m  Prop},
  motive n Nat.le.refl 
  ({m: Nat} (h: n  m), motive m h  motive m.succ (Nat.le.step h)) 
  {m: Nat} (h: n  m), motive m h

Lingwei Peng (彭灵伟) (Mar 08 2025 at 09:07):

I find this chain of reasoning quite complex: Decidable n = 0 → instDecidableEqNat → Nat.decEq → Nat.eq_of_beq_eq_true, and I think it is related to some implicit kernel behaviors. I understand the recursor, but I don’t fully understand why noConfusion can be derived from it. It seems more like a language rule for inductive types rather than a theorem (such as injectivity and disjointness of constructors).

Kevin Buzzard (Mar 08 2025 at 09:08):

The recursor is a much more powerful tool than it initially appears. Peano's original axioms included things like zero != n.succ and injectivity of succ but they can all be derived from the recursor

suhr (Mar 08 2025 at 09:10):

colorlessboy said:

It seems more like a language rule for inductive types rather than a theorem (such as injectivity and disjointness of constructors).

Remember that the Lean core is not a logic but a theory of constructions. Propositions and proofs are merely special cases of mathematical constructions.

suhr (Mar 08 2025 at 09:21):

Here's an example for booleans with some annotations:

#print Bool.noConfusionType

#reduce Bool.noConfusionType False false false  -- False → False
#reduce Bool.noConfusionType False false true   -- False
#reduce Bool.noConfusionType False true false   -- False
#reduce Bool.noConfusionType False true true    -- False → False

def bool_d {P: Sort u}(b: Bool): Bool.noConfusionType P b b :=
  b.casesOn
    ((λp  p) : Bool.noConfusionType P false false)
    ((λp  p) : Bool.noConfusionType P true true)

example (h: false = true): False :=
  -- Bool.noConfusionType False false true ≡ False
  show Bool.noConfusionType False false true from
    Eq.subst
      (motive := λt  Bool.noConfusionType _ false t)
      h
      (bool_d false : Bool.noConfusionType False false false)

Computationally equal types are the same... and that's the key for noConfusion.

Lingwei Peng (彭灵伟) (Mar 08 2025 at 10:02):

Computationally equal types are the same. This is clear. But I cannot understand the other part: why computationally different types are considered to be different.

I think the second part is more like an implicit axiom implemented in the kernel, rather than a theorem. But I am not sure.

The Lean 4 kernel has many implicit axioms that are not explicitly coded. For example:

example (a : Nat) : Quot.lift f f_respects (Quot.mk mod7Rel a) = f a :=
  rfl

This rfl works because the kernel automatically simplifies Quot.lift f f_respects (Quot.mk mod7Rel a) into f a, which looks like a provable theorem but is actually an axiom.

suhr (Mar 08 2025 at 10:28):

It's again not an axiom but a computation rule. These are indeed are documented rather poorly in Lean.

suhr (Mar 08 2025 at 10:32):

The Lean Reference merely mentions the reduction rule for inductive types: https://lean-lang.org/doc/reference/latest//The-Type-System/Inductive-Types/#iota-reduction. And I don't see a reduction rule for quotients in the reference.

Sebastian Ullrich (Mar 08 2025 at 10:38):

"Reduction" is mentioned many times in the quotient chapter

suhr (Mar 08 2025 at 10:38):

Oh, it's mentioned en passant:

https://lean-lang.org/doc/reference/latest//The-Type-System/Quotients/#Quot___sound

In addition to the above constants, Lean's kernel contains a reduction rule for Quot.lift that causes it to reduce when used with Quot.mk, analogous to ι-reduction for inductive types. Given a relation r over α, a function f from α to β, and a proof resp that f respects r, the term Quot.lift f resp (Quot.mk r x) is definitionally equal to f x.

suhr (Mar 08 2025 at 10:42):

By the way, Coq documentation is more straightforward: it lists all reduction rules in a separate section, and gives an example for ι-reduction while discussing inductive types.

Lingwei Peng (彭灵伟) (Mar 08 2025 at 10:52):

Yes, it is quite complex for beginners like me. When I eagerly look into which axioms are used after a theorem, I often encounter things I can’t fully grasp. The result is usually some kernel behavior hidden in the corners of the documentation.

Thanks for the Coq documentation.

I think noConfusion might be more complicated than the reduction example above. I’ll need more time to understand it.

suhr (Mar 08 2025 at 11:01):

Nat.noConfusion and Bool.noConfusion are very similar (you can see this by #print Bool.noConfusion and #print Nat.noConfusion) so if you understand how the example for Bool works you will be able to do the same for Nat.

Thanks for the Coq documentation.

Keep in mind that theories of Coq and Lean are slightly different: for example, Lean uses recursors while Coq uses fixpoints.

Lingwei Peng (彭灵伟) (Mar 08 2025 at 12:31):

suhr said:

Nat.noConfusion and Bool.noConfusion are very similar (you can see this by #print Bool.noConfusion and #print Nat.noConfusion) so if you understand how the example for Bool works you will be able to do the same for Nat.

Thanks for the Coq documentation.

Keep in mind that theories of Coq and Lean are slightly different: for example, Lean uses recursors while Coq uses fixpoints.

Thanks a lot! The Bool example is very clear.


Last updated: May 02 2025 at 03:31 UTC