Zulip Chat Archive

Stream: lean4

Topic: isDefEq failing on unifiable types


Jeremy Salwen (May 06 2023 at 17:38):

See the following MWE:

import Lean
import Std.Data.Array.Init.Lemmas
import Mathlib.Lean.Meta.Simp
import Lean.Elab.Term

open Lean Meta Elab Term Lean.Meta Tactic

def upLemma (f : Expr) : MetaM Expr := do
 let template  mkArrow (mkApp (mkConst ``List []) ( mkFreshTypeMVar)) ( mkFreshTypeMVar)
 logInfo ( inferType f)
 logInfo template
 if ¬ ( isDefEq ( inferType f) template) then
      throwError "Failed isDefEq"
 pure f

elab "upLemma% " f:term : term => do
  upLemma ( elabTerm f none)

def z := upLemma% List.toArray

the `isDefEq is failing, despite the types looking equal to me, after unification:

List ?m.4500  Array ?m.4500
List ?m.4801  ?m.4803

Maybe this is universe related? Both metavariables in the first expression are of typeType, but the metavariables in the second expression are of type Sort?

Henrik Böving (May 06 2023 at 17:43):

How do these types look unifiable to you at all? One is a function from List the other is a function from Nat

Jeremy Salwen (May 06 2023 at 17:46):

Ah, apologies, I introduce that mistake when minimizing the example, let me fix it.

Jeremy Salwen (May 06 2023 at 17:57):

Ok, fixed it.

Gabriel Ebner (May 06 2023 at 18:59):

mkConst ``List [] does not type check because it's missing a universe parameter. It should be mkConst ``List [u].

Jeremy Salwen (May 06 2023 at 19:24):

Ah ok, again it was a failure of minimizing the example. Here is something much closer to my original code. The weird behavior is that ftype will not unify with template2 (the actual thing I am trying to fix), but it will unify with template1, however, template1 will unify with template2. So isDefEq is not behaving transitively?

import Lean
import Std.Data.Array.Init.Lemmas
import Mathlib.Lean.Meta.Simp
import Lean.Elab.Term

open Lean Meta Elab Term Lean.Meta Tactic

def upLemma (f w : Expr) : MetaM Expr := do
 mapForallTelescope' (fun ty e => do
  match ty.getAppFnArgs with
  | (``Eq, #[α, _, _]) => do
    let ftype  inferType f
    let template1  mkArrow (mkApp (mkConst ``List [ mkFreshLevelMVar]) ( mkFreshTypeMVar)) ( mkFreshTypeMVar)
    let template2  mkArrow α ( mkFreshTypeMVar)

    if ¬ ( isDefEq ftype template2) then
      throwError "Failed to unify"
    pure e
  | x => throwError ""
 ) w

elab "upLemma% " f:term " x " e:term: term => do
  upLemma ( elabTerm f none) ( elabTerm e none)

def z := upLemma% List.toArray x @Array.push_data

Jeremy Salwen (May 07 2023 at 01:29):

I'm investigating some more, and I realize that this is probably related to mapForallTelescope' turning the universe parameter to Array.push_data into a concrete parameter, which may be causing weirdness. I'm going to investigate further.

Jeremy Salwen (May 07 2023 at 05:33):

Ok, I think what is going on is that it is refusing to unify a metavariable with a free variable. Here is my MWE:

import Lean
import Std.Data.Array.Init.Lemmas
import Mathlib.Lean.Meta.Simp
import Lean.Elab.Term

open Lean Meta Elab Term Lean.Meta Tactic
def upLemma (f : Expr) : MetaM Expr := do
  withLocalDecl `α BinderInfo.default (mkSort levelOne) (λ x => do
    let listTypeMetavar := f.getAppArgs[0]!
    logInfo listTypeMetavar
    logInfo x
    if ¬ ( isDefEq listTypeMetavar x) then
      throwError "wtf"
    pure (mkConst ``Nat [])
 )

elab "upLemma% " f:term : term => do
  upLemma ( elabTerm f none)

def z := upLemma% List.toArray

listTypeMetavar will happily unify with Nat, but it won't unify with α. I am not 100% sure what is going on here, but it seems like the problem is that the metavariable was introduced too early, so it cannot depend on free variables that were introduced later.

So I need to replace my metavariable in f with a fresh metavariable for this to work?

Jeremy Salwen (May 07 2023 at 05:41):

https://leanprover.zulipchat.com/#narrow/stream/270676-lean4/topic/.E2.9C.94.20Unification.20with.20.60isDefEq.60
seems to confirm that this is not allowed.

I was looking for a helper function in mathlib to substitute metavariables with fresh ones., but couldn't find it. Does something like this exist?

Jeremy Salwen (May 07 2023 at 19:11):

Ok, I seem to have fixed it! I had to write the helper function to refresh the metavariables` myself:

import Lean
import Std.Data.Array.Init.Lemmas
import Mathlib.Lean.Meta.Simp
import Lean.Elab.Term

open Lean Meta Elab Term Lean.Meta Tactic

def replaceRecM [Monad M] [MonadLiftT MetaM M] [MonadControlT MetaM M] (f? : (Expr  M Expr)  Expr  M (Option Expr)) : Expr  M Expr
  | expr => do
    memoFix (fun r e   do
      match  f? r e with
      | some x => pure x
      | none   => traverseChildren (M := M) r e) expr


partial def replaceMvarsWithFreshMVars: Expr  MetaM Expr :=
  replaceRecM (λ _ term => do
    match term with
    | .mvar id => some <$> (mkFreshExprMVar ( id.getType))
    | _ => pure none
  )

def upLemma (f w : Expr) : MetaM Expr := do
 mapForallTelescope' (fun ty e => do
  let fnew  replaceMvarsWithFreshMVars f
  match ty.getAppFnArgs with
  | (``Eq, #[α, _, _]) => do
    let ftype  inferType fnew
    let template1  mkArrow (mkApp (mkConst ``List [ mkFreshLevelMVar]) ( mkFreshTypeMVar)) ( mkFreshTypeMVar)
    let template2  mkArrow α ( mkFreshTypeMVar)

    if ¬ ( isDefEq ftype template2) then
      throwError "Failed to unify"
    pure e
  | x => throwError ""
 ) w


elab "upLemma% " f:term " x " e:term: term => do
  upLemma ( elabTerm f none) ( elabTerm e none)

def z := upLemma% List.toArray x @Array.push_data

Jannis Limperg (May 08 2023 at 13:26):

I'm a bit suspicious of this solution. Why can't you create the mvar with the correct local context (i.e. such that it has access to all relevant local variables) in the first place?

Jeremy Salwen (May 08 2023 at 15:48):

Jannis Limperg said:

I'm a bit suspicious of this solution. Why can't you create the mvar with the correct local context (i.e. such that it has access to all relevant local variables) in the first place?

In this case it was due to the metavariables being created outside of the function call. I can simplify things later, I just wanted to verify that I was actually understanding the cause of the failure.

Jannis Limperg (May 08 2023 at 16:13):

Fair. Your diagnosis seems correct to me. You can verify it by inspecting the local contexts of the mvars, which contain exactly the fvars which can be used in expressions assigned to the mvars.


Last updated: Dec 20 2023 at 11:08 UTC