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 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?

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: Dec 20 2023 at 11:08 UTC