Zulip Chat Archive
Stream: lean4
Topic: PSA: `Expr.abstract` does not do what you think it does
Gabriel Ebner (May 19 2021 at 08:15):
I'm happy about suggestions for a better term than unsafe. "Considered harmful" was my other choice, but I think this would come across even worse.
Mario Carneiro (May 19 2021 at 08:15):
"incorrect" seems the most accurate
Mario Carneiro (May 19 2021 at 08:15):
although as you point out it's not clear that a correct function with the same signature can exist
Gabriel Ebner (May 19 2021 at 08:19):
Daniel Fabian said:
I suggest creating an mwe and a github issue for how this be solved best in lean 4.
I am very reserved with suggesting library changes in Lean 4 these days. This is the function that I would have expected:
diff --git a/src/Lean/Meta/Basic.lean b/src/Lean/Meta/Basic.lean
index cd6a7fc920..fcbaa584be 100644
--- a/src/Lean/Meta/Basic.lean
+++ b/src/Lean/Meta/Basic.lean
@@ -434,6 +434,14 @@ def instantiateLocalDeclMVars (localDecl : LocalDecl) : MetaM LocalDecl := do
setNGen newS.ngen;
throwError "failed to create binder due to failure when reverting variable dependencies"
+/-- Similar to `abstract`, but consider only the first `min n xs.size` entries in `xs`. -/
+def abstractRange (e : Expr) (n : Nat) (xs : Array Expr) : MetaM Expr :=
+ liftMkBindingM <| fun lctx => MetavarContext.MkBinding.abstractRange xs n e false
+
+/-- Replace free variables `xs` with loose bound variables. -/
+def abstract (e : Expr) (xs : Array Expr) : MetaM Expr :=
+ abstractRange e xs.size xs
+
def mkForallFVars (xs : Array Expr) (e : Expr) (usedOnly : Bool := false) (usedLetOnly : Bool := true) : MetaM Expr :=
if xs.isEmpty then pure e else liftMkBindingM <| MetavarContext.mkForall xs e usedOnly usedLetOnly
diff --git a/src/Lean/MetavarContext.lean b/src/Lean/MetavarContext.lean
index f69a69c62a..61be8edf1f 100644
--- a/src/Lean/MetavarContext.lean
+++ b/src/Lean/MetavarContext.lean
@@ -948,7 +948,7 @@ partial def revert (xs : Array Expr) (mvarId : MVarId) : M (Expr × Array Expr)
contain a metavariable `?m` s.t. local context of `?m` contains a free variable in `xs`.
`elimMVarDeps` is defined later in this file. -/
-@[inline] private def abstractRange (xs : Array Expr) (i : Nat) (e : Expr) : M Expr := do
+@[inline] def abstractRange (xs : Array Expr) (i : Nat) (e : Expr) : M Expr := do
let e ← elimMVarDeps xs e
pure (e.abstractRange i xs)
Daniel Fabian (May 19 2021 at 08:25):
@Gabriel Ebner I was not proposing a PR, rather a bug. You can clearly add your change as a description of the problem. My point was about making sure we can have a good solution in lean4. And as I mentioned, there's probably a good reason for the function to be private.
It is difficult for me to judge if the behaviour you are experiencing is expected, e.g. because it's a perf trade off or something, or an oversight. Hence the issue.
Mario Carneiro (May 19 2021 at 08:28):
there's probably a good reason for the function to be private
I find it more likely that the developers just make things private by habit and make them public only when needed. That's certainly a habit I get into when writing in languages with pervasive module privacy like java or rust.
Mario Carneiro (May 19 2021 at 08:31):
I think it is possible to justify Expr.abstract
as written if it has a precondition that the expr in question has no metavariables, for example because it is already fully elaborated and pulled from an earlier definition
Daniel Fabian (May 19 2021 at 08:39):
and my first guess from reading the delta is that there may be a perf issue latent in the other version.
Last updated: Dec 20 2023 at 11:08 UTC