Zulip Chat Archive
Stream: new members
Topic: calc and grw with OrderIso
Eric Paul (Jul 08 2025 at 17:19):
I often have some chain of docs#OrderIso that I want to construct and apply. It would be more readable if I could use grw and calc to construct these. In particular, is it possible to make something like either of these examples work?
import Mathlib
example [LinearOrder A] [LinearOrder B] [LinearOrder C]
(h1 : A ≃o B) (h2 : B ≃o C) : A ≃o C :=
calc A
_ ≃o B := h1
_ ≃o C := h2
import Mathlib
example [LinearOrder A] [LinearOrder B] [LinearOrder C]
(h2 : B ≃o C) : A ⊕ₗ B ≃o A ⊕ₗ C := by
grw [h2]
My understanding is that for calc to work, I need an instance of docs#Trans. However, it seems that requires a relation where I can't enforce that the two types being related have an instance of LE and so I can't create such an instance for OrderIso.
Separately, my attempts at tagging things with @[gcongr] in hopes that grw would work were also unsuccessful.
Has anyone tried something along these lines?
Eric Paul (Jul 09 2025 at 20:32):
Here's an approach for the rewriting that I think works well enough for me. I'm surprised the orderIso_of_iso is not already in mathlib.
import Mathlib
variable [LinearOrder A] [LinearOrder B] [LinearOrder C]
open CategoryTheory
open LinOrd
def orderIso_of_iso (iso : of A ≅ of B) : A ≃o B where
toFun := iso.hom
invFun := iso.inv
left_inv := by simp [Function.LeftInverse]
right_inv := by simp [Function.RightInverse, Function.LeftInverse]
map_rel_iff' := by
intro a b
constructor
· intro h
by_contra!
change iso.hom a ≤ iso.hom b at h
have : iso.hom b ≤ iso.hom a := OrderHomClass.GCongr.mono iso.hom.hom' this.le
have : iso.hom a = iso.hom b := by order
apply_fun iso.inv at this
simp only [Iso.hom_inv_id_apply] at this
order
· exact fun a_1 ↦ OrderHomClass.GCongr.mono iso.hom.hom' a_1
example [LinearOrder A] [LinearOrder B] [LinearOrder C]
(h1 : A ≃o B) (h2 : B ≃o C) : A ≃o C := by
apply orderIso_of_iso
calc of A
_ ≅ of B := Iso.mk h1
_ ≅ of C := Iso.mk h2
Eric Paul (Jul 09 2025 at 21:35):
In order to try to use this approach with for grw, I needed to create this new notation otherwise @[gcongr] would complain that the conclusion was not in the right form. After making that notation, I can get gcongr working, but not grw yet.
import Mathlib
variable [LinearOrder A] [LinearOrder B] [LinearOrder C]
open CategoryTheory
open LinOrd
def orderIso_of_iso (iso : of A ≅ of B) : A ≃o B := sorry
def sumLex (a b : LinOrd) : LinOrd := of (a ⊕ₗ b)
infix:90 " +ₗ " => sumLex
@[gcongr]
def sumLexCongr (α₁ : LinOrd) {β₁ β₂ : LinOrd}
(eb : β₁ ≅ β₂) : α₁ +ₗ β₁ ≅ α₁ +ₗ β₂ := by
apply LinOrd.Iso.mk
exact OrderIso.sumLexCongr (OrderIso.refl α₁) (orderIso_of_iso eb)
@[simp]
def sumLex_iso_sum : of (A ⊕ₗ B) = (of A) +ₗ (of B) := by rfl
example [LinearOrder A] [LinearOrder B] [LinearOrder C]
(h2 : B ≃o C) : A ⊕ₗ B ≃o A ⊕ₗ C := by
apply orderIso_of_iso
simp
gcongr
· exact LinOrd.Iso.mk h2
When I use grw [(LinOrd.Iso.mk h2 : of B ≅ of C)], I get the error which I don't understand
tactic 'apply' failed, could not unify the conclusion of the term
?r ?b ?c → ?r ?a ?c
with the goal
(of A +ₗ of C ≅ of A +ₗ of C) → (of A +ₗ of B ≅ of A +ₗ of C)
Eric Paul (Jul 10 2025 at 15:11):
I got the grw to work! I was just missing the following gcongr lemma
attribute [gcongr] Iso.trans
Eric Paul (Jul 13 2025 at 13:36):
As an update in case someone else in the future thinks about this, I tried switching over to this approach but all the little calls needed to make swapping over to LinOrd work cluttered things enough that I felt like it was not a gain in readability. So I went ahead and made some tactics instead so that I can now write the following and that works well for me.
def iso (A) (B) [LinearOrder A] [LinearOrder B] : A ≃o B := sorry
example [LinearOrder A] [LinearOrder B] [LinearOrder C] [LinearOrder D] [LinearOrder E]
(h : C ≃o D) : (A ⊕ₗ B) ⊕ₗ C ≃o E ⊕ₗ B ⊕ₗ D := by
orderCalc (A ⊕ₗ B) ⊕ₗ C
_ ≃o A ⊕ₗ B ⊕ₗ C := by orderCongr
_ ≃o E ⊕ₗ B ⊕ₗ D := by orderCongr [iso A E]
Alex Brodbelt (Dec 27 2025 at 13:05):
I was thinking about the same for MulEquiv and MonoidHom, since I am also working with chains of maps.
I found this open issue #6509 and I believe it is just a matter of adding Trans instances. For instance (pun not intended):
import Mathlib
variable (A B C : Type*) [MulOne A] [MulOne B] [MulOne C]
instance : Trans
(@MonoidHom A B)
(@MonoidHom B C)
(@MonoidHom A C)
where trans f g := g.comp f
instance : Trans
(@MulEquiv A B)
(@MulEquiv B C)
(@MulEquiv A C)
where trans f g := f.trans g
-- Having issues with this one
-- instance : Trans
-- (@MulEquiv A B)
-- (@MonoidHom B C)
-- (@MonoidHom A C)
-- where trans f g := g.comp f.toMonoidHom
--now works!
noncomputable example : ℤ →* ℝ :=
calc
ℤ →* ℚ := MonoidHom.smulOneHom
ℚ →* ℝ := MonoidHom.smulOneHom
ℝ →* ℝ := {
toFun := fun x => abs x
map_one' := by simp
map_mul' := by simp
}
--now works!
example : Multiplicative (ZMod 2) ≃* Multiplicative (ZMod 2) :=
calc
Multiplicative (ZMod 2) ≃* Multiplicative (ZMod 2) := sorry
Multiplicative (ZMod 2) ≃* Multiplicative (ZMod 2) := sorry
Alex Brodbelt (Dec 27 2025 at 13:10):
I think I'll try open a PR for some of these if not all (I wonder if there is some further abstraction which can be done so this does not have to be done manually for each one) - I don't quite understand why you're not meant to provide the last two(?) implicit parameters, so maybe someone could enlighten me about this?
Aaron Liu (Dec 27 2025 at 15:00):
I feel like it's a bit hacky
Aaron Liu (Dec 27 2025 at 15:00):
calc is chaining the typeclasses instead of the types like it should be doing
Alex Brodbelt (Dec 27 2025 at 16:08):
whether it is hacky or not, I would find it useful :thinking: . I don't mind if it should be another tactic or not, though to me it would make sense to extend calc's capabilities
Last updated: Feb 28 2026 at 14:05 UTC