Zulip Chat Archive

Stream: new members

Topic: How to track down which prop is undecidable?


aron (Oct 02 2025 at 20:12):

I'm getting this error in my definition of a function:

failed to compile definition, consider marking it as 'noncomputable' because it depends on 'Classical.propDecidable', which is 'noncomputable'

I do indeed switch on 2 props in if expressions but I have defined Decidable instances for both kinds of props I am switching on. Is there a way to get Lean to tell me more precisely about which prop decidable it has a problem with? Atm the error is on the top-level function which is not very helpful for debugging purposes

Aaron Liu (Oct 02 2025 at 20:13):

just don't have Classical.propDecidable as an instance?

Robin Arnez (Oct 02 2025 at 20:13):

Did you open Classical somewhere? Try removing that

aron (Oct 02 2025 at 20:14):

No? not as far as I know?

aron (Oct 02 2025 at 20:15):

I suppose I do reference Classical.exists_or_forall_not somewhere, but I haven't opened the Classical namespace anywhere

Robin Arnez (Oct 02 2025 at 20:15):

Hmm, what does your definition look like?

Robin Arnez (Oct 02 2025 at 20:15):

Maybe it's something else in the function?

aron (Oct 02 2025 at 20:19):

instance ResolvableUniVarInTy.decide (ctx : List TyMaybe') (ty : Ty') (uv : Nat) : Decidable (ResolvableUniVarInTy ctx ty uv) := by
  if h : uv < ctx.length then
    let target := ctx.reverseGet uv h
    have iari : IsAtRevIndex uv ctx target := IsAtRevIndex.reverseGet_from_prf h
    cases' htarget : target with uvTy
    · rw [htarget] at iari
      if hh : uv  ty then
        if hhh : TyResolved ctx uvTy then
          refine .isTrue ?_
          refine .mk hh iari hhh
        else
          refine .isFalse ?_
          intro ruvit
          cases ruvit
          expose_names
          have := iari.injective h_2
          subst_eqs
          contradiction
      else
        refine .isFalse ?_
        intro ruvit
        cases ruvit
        contradiction
    · refine .isFalse ?_
      intro ruvit
      cases' ruvit with target hin iariconc
      · rw [htarget] at iari
        have := iari.injective iariconc
        contradiction
  else
    refine .isFalse ?_
    intro ruvit
    cases' ruvit with target hin iari
    absurd h
    refine iari.isLt

and I have both:

instance Ty'.dec_mem_uniVar (ty: Ty') (i : Nat) : Decidable (i  ty)

and

instance TyResolved.decide (ctx : List TyMaybe') (ty : Ty') {prf :  i  ty, i < ctx.length} : Decidable (TyResolved ctx ty)

aron (Oct 02 2025 at 20:20):

Hm could it be that TyResolved.decide requiring an extra param {prf : ∀ i ∈ ty, i < ctx.length} is getting in the way of decidability?

aron (Oct 02 2025 at 20:43):

Yeah I think it was that. I don't know if there's a way to pass in an implicit param needed by my Decidable instance to the if expression so I replaced the:

if hhh : TyResolved ctx uvTy then

with a:

have :  i  uvTy, i < ctx.length := by ...

match @TyResolved.decide ctx uvTy this with
| .isTrue prf => ...
| .isFalse contra => ...

i.e. I explicitly call the decide function with all needed params and then match on the cases, the error goes away :tada:

Robin Arnez (Oct 02 2025 at 21:11):

Oh, you defined it with tactics. I wouldn't recommend that because, among other things, that makes it open Classical in the conditions. Try something like this:

instance ResolvableUniVarInTy.decide (ctx : List TyMaybe') (ty : Ty') (uv : Nat) : Decidable (ResolvableUniVarInTy ctx ty uv) :=
  if h : uv < ctx.length then
    let target := ctx.reverseGet uv h
    have iari : IsAtRevIndex uv ctx target := IsAtRevIndex.reverseGet_from_prf h
    match target with
    | .whatever_your_thing_is_called uvTy =>
      if hh : uv  ty then
        if hhh : TyResolved ctx uvTy then
          isTrue (.mk hh iari hhh)
        else
          isFalse ?case1
      else
        isFalse ?case2
    | .the_other_thing_you_have =>
      isFalse ?case3
  else
    isFalse ?case4
where finally
  case case1 =>
    intro ruvit
    cases ruvit
    expose_names
    have := iari.injective h_2
    subst_eqs
    contradiction
  case case2 =>
    intro ruvit
    cases ruvit
    contradiction
  case case3 =>
    intro ruvit
    cases' ruvit with target hin iariconc
    · rw [htarget] at iari
      have := iari.injective iariconc
      contradiction
  case case4 =>
    intro ruvit
    cases' ruvit with target hin iari
    absurd h
    refine iari.isLt

A lot easier to read because of the definition/proof separation and more robust (this version should give you the Decidable error)

aron (Oct 02 2025 at 21:18):

oh interesting :thinking: I haven't seen this pattern before

Robin Arnez (Oct 02 2025 at 21:20):

It's quite recent but it's also a very good tool for definition readability

aron (Oct 02 2025 at 21:28):

mm yeah it's nice

aron (Oct 02 2025 at 21:28):

ok yeah I'm now getting the error at the right place!

failed to synthesize
  Decidable (TyResolved ctx uvTy)

aron (Oct 02 2025 at 21:30):

but although I can now see the error in the right place I still need to call TyResolved.decide explicitly I think. So I can pass in the implicit param correctly

aron (Oct 02 2025 at 21:31):

Is there a way to pass in the implicit param with the if h : <myProp> then syntax?

aron (Oct 02 2025 at 21:32):

Robin Arnez said:

because, among other things, that makes it open Classical in the conditions.

why does it do this btw? seems like an odd choice

aron (Oct 02 2025 at 21:32):

(...no pun intended)

Robin Arnez (Oct 02 2025 at 21:47):

It's just notation for by_cases and you usually don't want to see decidability errors in your proofs

Kenny Lau (Oct 03 2025 at 10:01):

basically try to avoid using tactics in definitions


Last updated: Dec 20 2025 at 21:32 UTC