Zulip Chat Archive
Stream: mathlib4
Topic: Module system weirdness
David Loeffler (Feb 14 2026 at 14:16):
I just ran into a fascinating bug: if you take any of the dozens of lemmas in Mathlib that involve the complex norm function, and use the extract_goal tactic somewhere in the proof, then instead of printing the current goal, it gives a mystery error message:
Unknown constant `_private.Mathlib.Analysis.Complex.Norm.0.Complex.norm_nonneg`
The diagnosis seems to be the following:
- As a step in building the
NormedAddCommGroupinstance onℂ, there is a private lemmaComplex.norm_nonneg, which is flagged asprivatebecause it is no longer needed once the instance is complete (the root-levelnorm_nonneglemma takes over). - However,
Complex.norm_nonnegis used for one of the fields of the instanceComplex.isAbsoluteValueNorm, resulting in a public declaration that relies on a private one (against the rules of the module system). - For some reason Lean doesn't complain about this at the point where
Complex.isAbsoluteValueNormis declared, but it causesextract_goalto complain if it is used in any later proof where complex numbers occur!
These mystery errors go away if we make Complex.norm_nonneg public; or if we conceal the use of a private lemma inside a tactic block, changing
instance isAbsoluteValueNorm : IsAbsoluteValue (‖·‖ : ℂ → ℝ) where
abv_nonneg' := norm_nonneg
...
to
instance isAbsoluteValueNorm : IsAbsoluteValue (‖·‖ : ℂ → ℝ) where
abv_nonneg' := by exact norm_nonneg
...
I'm very puzzled about how this is supposed to work.
- Why is it forbidden to use private lemmas as proof fields in
defs? I would have expected that since we have proof-irrelevance, in particular it should be irrelevant whether proofs are private or not. - If it's forbidden to do this in
def's, why is it permitted ininstances? (Edit: maybe Lean allows it if theinstanceis not data-carrying, i.e. lives inProprather thanType*?) - Why does
:= by exact foowork if:= foodoes not? - Why does the error only manifest itself when using
extract_goal?
Johan Commelin (Feb 16 2026 at 07:39):
by exact foo makes the proof private instead of exposing it. That's (half of?) an answer to your 3rd q.
I hope others can step in with answering the rest.
David Loeffler (Feb 16 2026 at 07:52):
Johan Commelin said:
by exact foomakes the proof private instead of exposing it. That's (half of?) an answer to your 3rd q.
Since proofs are definitionally singletons in Lean's logic, what does it mean to "expose" a proof? Surely "revealing that a proof exists without revealing its implementation" is a meaningless distinction in the presence of proof irrelevance?
Johan Commelin (Feb 16 2026 at 08:02):
From the kernel's point of view, yes. But not from the point of view of other parts of the system. Eg, a tactic could inspect what an exposed proof looks like, if I understand correctly.
(In other words, in theory we could implement a using_the_same_strategy_as myFancyProof tactic.)
David Loeffler (Feb 16 2026 at 08:19):
Johan Commelin said:
(In other words, in theory we could implement a
using_the_same_strategy_as myFancyProoftactic.)
But such a tactic would be pretty useless if it couldn't see inside a tactic block, wouldn't it? I mean, if myFancyProof is really all that fancy, it is bound to have a by in it somewhere.
Johan Commelin (Feb 16 2026 at 08:32):
Fair enough. It also wasn't a serious idea. Just trying to explain that different parts of the system have different povs on proof irrelevance. There might be better examples out there. But I'm not enough of an expert.
David Loeffler (Feb 16 2026 at 08:51):
I think distinguishing between "exposed" and "non-exposed" even for proofs is a bit silly; but even if that's the design intention here, then something is still wrong, because the IsAbsoluteValue instance is somehow exposing information that should be private (without raising an error in the process).
Last updated: Feb 28 2026 at 14:05 UTC