Zulip Chat Archive

Stream: mathlib4

Topic: stuck typeclass problem in try/catch


Matthew Ballard (Feb 07 2023 at 19:27):

In mathlib4#2109, I made an attempt at porting slice and its descendants. While it is not the most pretty, I am reasonably confident it is executing properly, at least on rewrites.

I am encountering a typeclass instance problem is stuck error which I do not completely understand.

For my best an awful #mwe example, see below. I have included a similar example in Slice.lean in the branch and have included a worked-on Equivalence.lean file with similar behavior (sliceLHS can be used to solve the goal modulo this).

import Mathlib.CategoryTheory.Category.Basic
import Mathlib.Tactic.Conv

open CategoryTheory
open Lean Parser.Tactic Elab Command Elab.Tactic Meta

def test : TacticM Unit := do
   iterateUntilFailure do
    ``(Category.assoc) >>= fun e => rewriteTarget' e (symm := false)

elab "test" : tactic => test

variable (C : Type) [Category C] (X Y Z W : C)
variable (f : X ⟶ Y) (g : Y ⟶ Z) (h : Z ⟶ W)

example : (f ≫ g) ≫ h = ((f ≫ g) ≫ h := by
  test — typeclass instance problem is stuck
  rfl

Replacing iterateUntilFailure with iterateAtMost 1removes the error. iterateAtMost 2 brings it back.

Matthew Ballard (Feb 07 2023 at 19:28):

I can try to minimize this further later this evening after teaching/dinner but it appears the error is coming from the catch branch in the iteration tactics.

Matthew Ballard (Feb 07 2023 at 19:34):

unit_inverse_comp in the equivalences file in that branch is a fun illustration.

Thomas Murrills (Feb 07 2023 at 21:11):

Interesting! (Btw, I've just moved the test from Slice.lean into test/slice.lean, where it can stay forever as a useful test of this issue!)

Matthew Ballard (Feb 08 2023 at 01:46):

Here is a better #mwe:

import Lean.Elab.Tactic
open Lean Parser.Tactic Elab Command Elab.Tactic Meta

class Assoc (α : Type) extends Mul α where
  assoc :  a₁ a₂ a₃ : α , (a₁ * a₂) * a₃ = a₁ * (a₂ * a₃)

variable [Monad m] [MonadExceptOf Exception m]

partial def iterateUntilFailure (tac : m Unit) : m Unit :=
  try tac; iterateUntilFailure tac catch _ => pure ()

def iterateAtMost : Nat  m Unit  m Unit
| 0, _ => pure ()
| n + 1, tac => try tac; iterateAtMost n tac catch _ => pure ()

def test : TacticM Unit := do
  iterateUntilFailure do
    evalTactic <|  `(conv| rw [Assoc.assoc])

elab "test" : conv => test

variable (α : Type) [Assoc α]

example (a₁ a₂ a₃ : α) : (a₁ * a₂) * a₃ = a₁ * (a₂ * a₃) := by
  conv =>
    lhs
    test -- typeclass instance problem is stuck, it is often due to metavariables Assoc ?m.7325

Add rfl to the example

example (a₁ a₂ a₃ : α) : (a₁ * a₂) * a₃ = a₁ * (a₂ * a₃) := by
  conv =>
    lhs
    test
  rfl -- no goals to be solved

Replacing iterateUntilFailure with iterateAtMost 1 removes the original error. With iterateAtMost 2 it returns.

Matthew Ballard (Feb 08 2023 at 02:09):

Specializing m to TacticM seems to have fixed the issue.

Adam Topaz (Feb 08 2023 at 02:56):

If you change MonadExceptOf to MonadExcept then it also works.

Adam Topaz (Feb 08 2023 at 02:56):

I guess the metavariable that lean is complaining about is Exception in the MonadExceptOf variable

Matthew Ballard (Feb 08 2023 at 02:57):

Is that where the Category metavariable would land?

Adam Topaz (Feb 08 2023 at 02:57):

I'm not sure what you mean

Matthew Ballard (Feb 08 2023 at 02:58):

Sorry. For this one, it complains about finding an instance of Assoc.

Adam Topaz (Feb 08 2023 at 02:58):

Oh

Adam Topaz (Feb 08 2023 at 02:59):

Okay now I'm confused. This also works:

import Lean.Elab.Tactic
open Lean Parser.Tactic Elab Command Elab.Tactic Meta

class Assoc (α : outParam Type) extends Mul α where
  assoc :  a₁ a₂ a₃ : α , (a₁ * a₂) * a₃ = a₁ * (a₂ * a₃)

variable [Monad m] [MonadExceptOf Exception m]

partial def iterateUntilFailure (tac : m Unit) : m Unit :=
  try tac; iterateUntilFailure tac catch _ => pure ()

def test : TacticM Unit := do
  iterateUntilFailure do
    evalTactic <|  `(conv| rw [Assoc.assoc])

elab "test" : conv => test

variable (α : Type) [Assoc α]

example (a₁ a₂ a₃ : α) : (a₁ * a₂) * a₃ = a₁ * (a₂ * a₃) := by
  conv =>
    lhs
    test

Adam Topaz (Feb 08 2023 at 03:00):

It seems that typeclass search just gets stuck even though it shouldn't?

Matthew Ballard (Feb 08 2023 at 03:01):

Yes. It seems so. Putting rfl at the end convinces Lean that all goals are cleared.

Adam Topaz (Feb 08 2023 at 03:01):

what's the set_option for typeclass tracing?

Thomas Murrills (Feb 08 2023 at 03:02):

Nice catch, because this is really weird to me. Even specifying the monad with iterateUntilFailure (m := TacticM) ... in the calls doesn't help—it actually has to be in the type declaration.

Matthew Ballard (Feb 08 2023 at 03:05):

set_option trace.Meta.synthInstance true only for #synth?

Adam Topaz (Feb 08 2023 at 03:06):

no that's the one

Adam Topaz (Feb 08 2023 at 03:07):

I don't really know why it's failing.

Matthew Ballard (Feb 08 2023 at 03:11):

(aside: slice is working well enough now that I am trying to kill off !4#1287 before bed)

Adam Topaz (Feb 08 2023 at 03:11):

Is there any reason why one should not just use MonadExcept here?

Matthew Ballard (Feb 08 2023 at 03:11):

I took the tactic iterators from Mathlib.Tactic.Corewithout much thought

Adam Topaz (Feb 08 2023 at 03:12):

Oh I see.

Matthew Ballard (Feb 08 2023 at 03:13):

But I doubt you would have much to break if you changed them there

Adam Topaz (Feb 08 2023 at 03:15):

I'll leave that to the tactic writing experts to decide.

Thomas Murrills (Feb 08 2023 at 03:21):

It's strange to me that making everything explicit doesn't work either. I wouldn't have expected this—but I guess that's the magic of outparams, which I don't understand yet.

def inst : MonadExceptOf Exception TacticM := inferInstance

partial def iterateUntilFailure' {m : Type _  Type u} [m₀ : Monad m] [m₁ : MonadExceptOf Exception m] (tac : m Unit) : m Unit :=
  try tac; @iterateUntilFailure' m m₀ m₁ tac catch _ => pure ()

def test : TacticM Unit := do
  @iterateUntilFailure' TacticM instMonadTacticM inst do
    rewriteTarget' ( ``(Category.assoc)) (symm := false)

-- same issues occur

Thomas Murrills (Feb 08 2023 at 03:25):

Btw, the defs in that block of Mathlib.Tactic.Core which depend on [MonadExceptOf Exception m] are only used in slice and tauto. If tauto works with the change, I would hope we could just change it to [MonadExcept Exception m]—but I'm not sure why that choice was made in the first place, so we should confirm with an expert.

Thomas Murrills (Feb 08 2023 at 03:30):

In any case, it's great that it's working! Thanks for porting it! :)

Thomas Murrills (Feb 08 2023 at 03:32):

In terms of git organization (which you might be planning to do already, but just mentioning since I don’t see it on GitHub): I think !4#1287 should be made to depend on !4#2109, and !4#2109 shouldn’t include Equivalence.

Matthew Ballard (Feb 08 2023 at 03:33):

Agreed. Once I get equivalences working or give up, I will clean and arrange things.

Thomas Murrills (Feb 08 2023 at 04:18):

Another thing you might be planning already anyway, but: I think we should go ahead and change MonadExceptOf to MonadExcept in that spot in Tactic.Core, check that nothing breaks, link to this thread in the PR description, and the change will get seen and confirmed by an expert during review.

Matthew Ballard (Feb 08 2023 at 15:45):

This should be done now. Please check my work.


Last updated: Dec 20 2023 at 11:08 UTC