Zulip Chat Archive
Stream: lean4
Topic: Reducible definition and TC problem
Tomas Skrivan (Jan 26 2022 at 18:03):
I'm having a trouble to synthesize an instance where one of the arguments is hidden behind a reducible definition. Such definition such be invisible to TC but it does not seems so in a specific case.
class Trait (X : Type u) where
R : Type v
attribute [reducible] Trait.R
class SemiInner (X : Type u) (R : Type v) where
semiInner : X → X → R
@[reducible] instance (X) (R : Type u) [SemiInner X R] : Trait X := ⟨R⟩
def norm {X} [Trait X] [inst : SemiInner X (Trait.R X)] (x : X) : Trait.R X := SemiInner.semiInner x x
section Real
def ℝ := Float
instance : SemiInner ℝ ℝ := ⟨λ x y : Float => x * y⟩
variable (x : ℝ)
set_option trace.Meta.synthInstance true in
#check (norm x : ℝ) -- error
-- These all work correctly
example : Trait.R ℝ = ℝ := by rfl
example : Trait ℝ := by infer_instance
example : Trait (Trait.R ℝ) := by infer_instance
end Real
section Float
instance : SemiInner Float Float := ⟨λ x y => x * y⟩
variable (x : Float)
#check (norm x : Float) -- this works
end Float
The problematic line is #check (norm x : ℝ)
The trace log ends with:
[Meta.synthInstance.newSubgoal] SemiInner ℝ (Trait.R ℝ)
[Meta.synthInstance.globalInstances] SemiInner ℝ (Trait.R ℝ), []
[Meta.synthInstance] failed
It looks like there is no instance for SemiInner ℝ (Trait.R ℝ) However, this example : SemiInner ℝ (Trait.R ℝ) := by infer_instance succeeds.
The example with Float instead of ℝ works fine. Which is even stranger. Do I misunderstand something or is this a bug?
Tomas Skrivan (Jan 26 2022 at 18:08):
One more thing to point out, it is important that the type ℝ is enforced.
#check norm x
works but type is Trait.R ℝ. This type should reduce to ℝ as Trait.R is marked reducible. However
#check (norm x : ℝ)
fails
Mario Carneiro (Jan 26 2022 at 18:24):
I don't think you can (usefully) mark constructors/projections as reducible
Leonardo de Moura (Jan 26 2022 at 18:24):
To understand the issue, we have to display the implicit arguments. You can do that using set_option pp.explicit true. The trace now is
[Meta.synthInstance.newSubgoal] SemiInner ℝ (@Trait.R ℝ (@Trait.mk ℝ Float))
[Meta.synthInstance.globalInstances] SemiInner ℝ (@Trait.R ℝ (@Trait.mk ℝ Float)), []
[Meta.synthInstance] failed
Note the Float in the trace above. You can fix this issue using
abbrev ℝ := Float
Mario Carneiro (Jan 26 2022 at 18:25):
That is, I don't think Trait.R ℝ = ℝ is a reducible defeq
Mario Carneiro (Jan 26 2022 at 18:28):
Assuming you didn't want to make ℝ reducible, I think another option would be instance : SemiInner ℝ Float := ⟨λ x y : Float => x * y⟩
Tomas Skrivan (Jan 26 2022 at 19:14):
Yes I don't want to make ℝ reducible.
Great, I didn't know about the option set_option pp.explicit true, it will be super useful to debug this.
Tomas Skrivan (Jan 26 2022 at 19:17):
One thing I do not understand is how did Float sneaked in to:
main goal SemiInner ℝ (@Trait.R ℝ (@Trait.mk ℝ Float))
Tomas Skrivan (Jan 26 2022 at 19:22):
If I define ℝ as inductive ℝ it works, i.e. it is some arbitrary type with no connection to Float.
Making the definition as @[irreducible] def ℝ := Float, still produced the goal:
main goal SemiInner ℝ (@Trait.R ℝ (@Trait.mk ℝ Float))
I really do not understand where the Float comes in.
Tomas Skrivan (Jan 26 2022 at 19:24):
Mario Carneiro said:
I don't think you can (usefully) mark constructors/projections as reducible
What do you mean by that? If you comment it out, the code breaks even more.
Tomas Skrivan (Jan 26 2022 at 19:36):
Ohh is reducible transitive? So because Trait.R is marked as reducible and if Trait.R X happens to be ℝ does it get reduced all the way to Float? Is this really the intended behavior?
Leonardo de Moura (Jan 26 2022 at 19:42):
One thing I do not understand is how did Float sneaked in to:
The elaborator has to solve typing constraints, and one of them is
ℝ =?= @Trait.R.{?u.166, ?u.165} ?m.167 ?m.168
You can see how Lean solves it using
set_option trace.Meta.isDefEq true in
#check (norm x : ℝ) -- error
The trace contains
[Meta.isDefEq.step] ℝ =?= @Trait.R.{?u.166, ?u.165} ?m.167 ?m.168
[Meta.isDefEq.delta.unfoldLeft] ℝ
[Meta.isDefEq.step] Float =?= @Trait.R.{?u.166, ?u.165} ?m.167 ?m.168
[Meta.isDefEq.delta.unfoldRight] Trait.R
[Meta.isDefEq.step] Float =?= ?m.168.1
[Meta.isDefEq.assign]
[Meta.isDefEq.assign] ?m.168 := @Trait.mk.{?u.166, ?u.165} ?m.167 Float
[Meta.isDefEq.assign.beforeMkLambda] ?m.168 [] := @Trait.mk.{?u.166, ?u.165} ?m.167 Float
[Meta.isDefEq.assign.checkTypes]
[Meta.isDefEq.step] Trait.{?u.166, ?u.165} ?m.167 =?= Trait.{?u.166, ?u.165} ?m.167
[Meta.isDefEq.assign.final] ?m.168 := @Trait.mk.{?u.166, ?u.165} ?m.167 Float
[Meta.isDefEq] ℝ =?= @Trait.R.{?u.166, ?u.165} ?m.167 (@Trait.mk.{?u.166, ?u.165} ?m.167 Float) ... success
Making the definition as @[irreducible] def ℝ := Float, still produced the goal:
I will take a look at this issue.
If I define ℝ as inductive ℝ it works, i.e. it is some arbitrary type with no connection to Float.
Yes, structures (or inductives) are the way to go if you want to "seal" ℝ
Example:
section Real
structure ℝ where
val : Float
instance : SemiInner ℝ ℝ := ⟨λ x y : ℝ => ℝ.mk (x.val * y.val) ⟩
variable (x : ℝ)
#check (norm x : ℝ) -- Works
Leonardo de Moura (Jan 26 2022 at 19:44):
Tomas Skrivan said:
Ohh is
reducibletransitive? So becauseTrait.Ris marked as reducible and ifTrait.R Xhappens to beℝdoes it get reduced all the way toFloat? Is this really the intended behavior?
See comment above. The Float is not exposed by the TC procedure, but the elaborator while solving the typing constraints for norm x.
Mario Carneiro (Jan 26 2022 at 19:45):
[Meta.isDefEq.step] ℝ =?= @Trait.R.{?u.166, ?u.165} ?m.167 ?m.168 [Meta.isDefEq.delta.unfoldLeft] ℝ [Meta.isDefEq.step] Float =?= @Trait.R.{?u.166, ?u.165} ?m.167 ?m.168 [Meta.isDefEq.delta.unfoldRight] Trait.R
Why did that happen? Trait.R is marked as reducible and ℝ is not, so it seems odd that it decided to unfold on the left first
Leonardo de Moura (Jan 26 2022 at 19:47):
@Mario Carneiro Trait.R is a projection, they have special treatment.
Leonardo de Moura (Jan 26 2022 at 19:56):
Making the definition as @[irreducible] def ℝ := Float, still produced the goal:
I will take a look at this issue.
https://github.com/leanprover/lean4/commit/2fff4c42b71fc1d451bf968a7f1a07d10993f2eb
Last updated: May 02 2025 at 03:31 UTC