Zulip Chat Archive
Stream: mathlib4
Topic: Different Instance on same structure
Andrés Goens (Jun 26 2023 at 17:32):
Sometimes a structure might have two plausible instances of a typeclass but one is obviously the canonical one, like PartialOrder Nat
from Nat.lt
when Dvd.dvd
could also define one. Sometimes there's no obvious one like CommMonoid
, and then people use different notions for the different instances like CommMonoid
and AddCommMonoid
e.g. in Nat
too. But what if it's somewhere in-between?
We've been looking at Bitvector
a bit and there there's different variants of a bunch of operations for both signed and unsigned bitvectors. Unsigned bitvectors seems to be a sensible default, for stuff typeclass instances like Preorder
(for which there's already an instance, albeit a pretty old one), but also things like ShiftRight
or even Div
are different for signed or unsigned bitvectors. It probably doesn't make sense to define their own typeclasses for the signed variants, it's not like it's something that arises all over like AddCommMonoid
. There seems to be four distinct options here:
- define no instances for anything and just prove stuff again if we need it
- define a new type (e.g.
SignedBitvector
) and prove everything twice for both types and have to deal with converting back and forth explicitly - define 1 default instance (here, concretely, for unsigned bitvectors) and treat the other version like a less important one
- define 2 instances and put them away
scoped
under a namespace, in the same spirit ofprotected
definitions.
I like the last one because if we do that, if we open that namespace, everything works seamlessly for that structure, and we can choose which structure we work with explicitly. @Chris Hughes pointed out that then sometimes say a rewrite might not work without passing an explicit typeclass instance, if the namespace is closed. I can see why this version of hiding things under a namespace might seem evil, but I wanted to hear other opinions, as none of the options seems clearly like the best here.
James Gallicchio (Jun 26 2023 at 17:35):
I'd definitely define a new type... would it really double the proofs? You already need to prove correctness for both signed and unsigned operations separately, no?
Andrés Goens (Jun 26 2023 at 17:38):
Sure, for things that are different. I guess for the ones that are the same the proofs would be fairly trivial by just unfolding the type alias. :thinking:
James Gallicchio (Jun 26 2023 at 17:40):
The proof might be defeq, even :)
James Gallicchio (Jun 26 2023 at 17:42):
You might even not have to copy the name into the new namespace, since I think the name resolution algorithm unfolds the head term if it can't find any matching names
Andrés Goens (Jun 26 2023 at 18:02):
James Gallicchio said:
You might even not have to copy the name into the new namespace, since I think the name resolution algorithm unfolds the head term if it can't find any matching names
that went a bit over my head :sweat_smile: meaning that MyType.someTheorem
would be found if I just write someTheorem
on an element of the alias? That'd be pretty cool indeed!
I guess I'm not sure what the best way is to define a type alias like that, as @abbrev
will make the definition reducible for typeclass synthesis. Looking around in Mathlib/GroupTheory/GroupAction/ConjAct.lean
there's a definition
/-- A type alias for a group `G`. `ConjAct G` acts on `G` by conjugation -/
def ConjAct : Type _ := G
But that doesn't seem to work e.g. if I try to copy ConjAct
:
def MyNat : Type _ := Nat
instance : LT MyNat := ⟨LT Nat⟩
/-
application type mismatch
{ lt := LT Nat }
argument
LT Nat
has type
Type : Type 1
but is expected to have type
MyNat → MyNat → Prop : Type
-/
James Gallicchio (Jun 26 2023 at 18:47):
Hm, I might be wrong. Let me search around zulip for a moment
James Gallicchio (Jun 26 2023 at 19:03):
def Foo := Unit
def Foo' := Foo
def Foo.bar (f : Foo) : Int := 0
def foo : Foo' := ()
#eval foo.bar
is what I was talking about
James Gallicchio (Jun 26 2023 at 19:04):
if you want to copy instances across defs, you generally have to do something like instance : Foo X := inferInstanceAs (FooDef X)
Chris Hughes (Jun 26 2023 at 22:06):
Try instance : LT MyNat := inferInstanceAs (LT Nat)
Last updated: Dec 20 2023 at 11:08 UTC