Zulip Chat Archive

Stream: general

Topic: Type class trouble


Michael Stoll (May 21 2022 at 21:43):

I am having trouble with the following.

import tactic.basic
import field_theory.finite.basic
import ring_theory.trace

def finite_field.trace_to_zmod (F : Type*) [field F] [fintype F] : F →ₗ[zmod (ring_char F)] zmod (ring_char F) :=
begin
  haveI := ring_char.char_p F,
  haveI := zmod.algebra F (ring_char F),
  let tr := algebra.trace (zmod (ring_char F)) F,
  exact tr, -- invalid type ascription ...
end

Before exact tr, the state looks like this:

F: Type ?
_inst_1: field F
_inst_2: fintype F
_inst: char_p F (ring_char F)
_inst_3: algebra (zmod (ring_char F)) F
tr: F →ₗ[zmod (ring_char F)] zmod (ring_char F) := algebra.trace (zmod (ring_char F)) F
 F →ₗ[zmod (ring_char F)] zmod (ring_char F)

Clicking on the type of tr in the infoview shows exactly the same information as clicking on the type of the goal:

linear_map
zmod (ring_char F)
zmod (ring_char F)
ring.to_semiring
ring.to_semiring
ring_hom.id (zmod (ring_char F))
F
zmod (ring_char F)
add_comm_group.to_add_comm_monoid F
add_comm_group.to_add_comm_monoid (zmod (ring_char F))
algebra.to_module
semiring.to_module

The mismatching types in the error message differ in the following places:

       (@ring.to_add_comm_group F
          (@comm_ring.to_ring F (@euclidean_domain.to_comm_ring F (@field.to_euclidean_domain F _inst_1)))))

vs.

       (@ring.to_add_comm_group F (@division_ring.to_ring F (@field.to_division_ring F _inst_1))))

and

       (@ring.to_semiring F
          (@comm_ring.to_ring F (@euclidean_domain.to_comm_ring F (@field.to_euclidean_domain F _inst_1))))
       _inst_3)

vs.

       (@ring.to_semiring F (@division_ring.to_ring F (@field.to_division_ring F _inst_1)))
       (@zmod.algebra F (@division_ring.to_ring F (@field.to_division_ring F _inst_1))
          (@ring_char F
             (@non_assoc_ring.to_non_assoc_semiring F
                (@ring.to_non_assoc_ring F (@division_ring.to_ring F (@field.to_division_ring F _inst_1)))))
          _))

It looks like part of the problem is that two differing paths are used to get from field to ring. I have no idea how to avoid this...

Eric Wieser (May 21 2022 at 21:49):

What doesconvert tr leave as a goal?

Eric Wieser (May 21 2022 at 21:50):

Does changing the second haveI to a letI help?

Eric Wieser (May 21 2022 at 21:53):

I think that's your problem; lean is saying "you told me to forget what _inst_3 is, so I can't unfold it any further to check it's equal to the other instance"

Michael Stoll (May 21 2022 at 21:57):

Thanks! I was forgetting that algebra ... is data.

Michael Stoll (May 21 2022 at 21:59):

A more helpful error message would go a long way...

Eric Wieser (May 21 2022 at 22:05):

We could make haveI detect this error

Eric Wieser (May 21 2022 at 22:05):

Do you even need the haveI there though?

Michael Stoll (May 21 2022 at 22:15):

Actually, no.

Eric Wieser (May 21 2022 at 22:16):

What made you think you did?

Eric Wieser (May 21 2022 at 22:16):

(that is, do we need to improve documentation somewhere?)

Michael Stoll (May 21 2022 at 22:18):

I think I thought I needed [char_p F (ring_char F)] for something. But it is possible that as the proof evolved, that changed.

Eric Wieser (May 21 2022 at 22:20):

Ah right, docs#ring_char.char_p is not an instance

Eric Wieser (May 21 2022 at 22:21):

So you have that automatically with no need for a haveI

Michael Stoll (May 21 2022 at 22:24):

Ah, I hadn't realized that. Good to know...

Eric Wieser (May 21 2022 at 22:28):

Having said that, the strategy of adding letI lines is a good one when lean can't find the instance through a long chain and you can't work out what condition you're missing

Eric Wieser (May 21 2022 at 22:30):

For instance, if it can't find addition on a product type, you add letI : has_add (X × Y) := prod.has_add and then your error message will tell you where the left or right half is missing addition

Michael Stoll (May 22 2022 at 13:29):

Thanks for the tip! I'll try to remember it.

Michael Stoll (May 22 2022 at 15:01):

Another question: I'm having trouble with something like the following.

import tactic.basic
import field_theory.finite.basic

structure test (A : Type) [comm_ring A] := (A' : Type) (B : comm_ring A') (C : A →+ A')

def test1 : test  := { A' := , B := _, C := add_monoid_hom.id  }

def test2 : test  :=
{ A' := test1.A', B := test1.B,
  C := test1.C.comp (add_monoid_hom.id _) }
-- failed to synthesize type class instance for
-- ⊢ add_zero_class test1.A'

def test3 : test  :=
{ A' := test1.A', B := test1.B,
  C := begin haveI := test1.B, exact test1.C.comp (add_monoid_hom.id _) end }
-- synthesized type class instance is not definitionally equal to expression inferred by typing rules, synthesized
--  add_monoid.to_add_zero_class test1.A'
-- inferred
--  add_monoid.to_add_zero_class test1.A'

How do I get the correct instance(s) into the context when filling the fields of the structure?

Yaël Dillies (May 22 2022 at 15:02):

What about

structure test (A : Type) [comm_ring A] := (A' : Type) [B : comm_ring A'] (C : A →+ A')

Michael Stoll (May 22 2022 at 15:05):

Still gives the same error for test2 (and test3).

Michael Stoll (May 22 2022 at 15:06):

In fact, test2 gives now the additional message

synthesized type class instance is not definitionally equal to expression inferred by typing rules, synthesized
  
inferred
  add_monoid.to_add_zero_class test1.A'

Michael Stoll (May 22 2022 at 15:31):

replacing comm_ring by add_zero_class to exclude possible problems from chaining of instances doesn't help. Instead of add_monoid.to_add_zero_class test1.A'the error messages now refer to test1.B, but they are still there:

import tactic.basic
import data.int.basic

structure test (A : Type) [add_zero_class A] := (A' : Type) [B : add_zero_class A'] (C : A →+ A')

def test1 : test  := { A' := , B := _, C := add_monoid_hom.id  }

def test2 : test  :=
{ A' := test1.A', B := _,
  C := test1.C.comp (add_monoid_hom.id _) }
-- failed to synthesize type class instance for
-- ⊢ add_zero_class test1.A'
--
-- synthesized type class instance is not definitionally equal to expression inferred by typing rules, synthesized
--   ⁇
-- inferred
--   test1.B

def test3 : test  :=
begin haveI := test1.B, exact
{ A' := test1.A', B := _,
  C := test1.C.comp (add_monoid_hom.id _) }
end
-- synthesized type class instance is not definitionally equal to expression inferred by typing rules, synthesized
--   _inst
-- inferred
--   test1.B

Michael Stoll (May 22 2022 at 15:33):

The red squiggly line is below C(in test1.C.comp) in test2, but below test1 in test3.

Damiano Testa (May 22 2022 at 15:49):

I do not know how to analyze this, but maybe this is useful:

import tactic.basic
import data.int.basic

--  B is now implicit, rather than typeclass
structure test (A : Type*) [add_zero_class A] := (A' : Type*) {B : add_zero_class A'} (C : A →+ A')

def test1 : test  := { A' := , B := _, C := add_monoid_hom.id  }

def test2 : test  :=
{ A' := test1.A', B := _,
--  use @, ugh, and the ℤ is important!
  C :=  @add_monoid_hom.comp _ _  _ _ _ test1.C (add_monoid_hom.id _) }

Reid Barton (May 22 2022 at 15:59):

Add attribute [instance] test.B after the definition of test, and get rid of the haveI tactic stuff in test3 (it creates a variable instance with no local definition)

Michael Stoll (May 22 2022 at 17:08):

Thanks, Reid.
Can you also explain why this is necessary?

Eric Wieser (May 22 2022 at 17:12):

When you write:

structure test (A : Type) [add_zero_class A] := (A' : Type) [B : add_zero_class A'] (C : A →+ A')

this means "make B a typeclass argument to test.mk"

Eric Wieser (May 22 2022 at 17:12):

It does not mean "use B when performing instance search"

Eric Wieser (May 22 2022 at 17:12):

To say that, you need to add the [instance] attribute; it helps if you remember that instance is roughly @[instance] def

Michael Stoll (May 22 2022 at 17:13):

OK. And why did the haveI ... version not work? The error message was quite confusing, claiming that two things are not defeq that look exactly the same.

Reid Barton (May 22 2022 at 17:16):

It is the usual letI vs haveI issue

Michael Stoll (May 22 2022 at 17:17):

Again! :oh_no:


Last updated: Dec 20 2023 at 11:08 UTC