Zulip Chat Archive
Stream: lean4
Topic: Transitive isAssigned
Marcus Rossel (Apr 11 2024 at 17:03):
When assigning an mvar m₂
whose type also contains an mvar m₁
, how can I check whether m₁
has been assigned (by virtue of m₂
being assigned)? I tried the following, but that leads to m₁
being considered unassigned:
import Lean
open Lean Meta Elab Term
elab "test" : term => do
let m₁ ← mkFreshExprMVar (Expr.sort 1)
println! ← m₁.mvarId!.getType
-- Type
let m₂ ← mkFreshExprMVar (Expr.app (.const ``List [← mkFreshLevelMVar]) m₁)
println! ← m₂.mvarId!.getType
-- List.{?_uniq.2855} ?_uniq.2854
let l ← elabTerm (← `(([] : List Nat))) none
let l ← instantiateMVars l
println! ← inferType l
-- List.{0} Nat
m₂.mvarId!.assign l
let m₂ ← instantiateMVars m₂
println! ← inferType m₂
-- List.{0} Nat
println! ← m₁.mvarId!.isAssigned
-- false
return mkNatLit 0
example := test
Marcus Rossel (Apr 12 2024 at 09:22):
Ahhh, the docs for docs#Lean.MVarId.assign hold the answer: "This is a low-level API, and it is safer to use isDefEq (mkMVar mvarId) x
" :see_no_evil:.
Mario Carneiro (Apr 12 2024 at 09:25):
That documentation is lying, don't do that
Mario Carneiro (Apr 12 2024 at 09:25):
I think there is an open issue about it
Mario Carneiro (Apr 12 2024 at 09:26):
The problem is that isDefEq (mkMVar mvarId) x
doesn't assign the mvar at all if it's a proof
Mario Carneiro (Apr 12 2024 at 09:26):
instead you should use assignIfDefeq
Mario Carneiro (Apr 12 2024 at 09:27):
Although, reading your question I don't see what it has to do with assign
Mario Carneiro (Apr 12 2024 at 09:28):
mvar.isAssigned
should return true if and only if mvar
is assigned. I don't really understand what you mean by "m₁
has been assigned (by virtue of m₂
being assigned)"
Mario Carneiro (Apr 12 2024 at 09:33):
I can't find assignIfDefeq
though, maybe it's too new (or the PR stalled)? In any case, in your code you need to make sure the types match before the assign
call, i.e.
let .true ← isDefEq (← inferType m₂) (← inferType l) | failure
m₂.mvarId!.assign l
Marcus Rossel (Apr 12 2024 at 09:41):
Mario Carneiro said:
mvar.isAssigned
should return true if and only ifmvar
is assigned. I don't really understand what you mean by "m₁
has been assigned (by virtue ofm₂
being assigned)"
Then it seems I don't understand what it means for an mvar m
to be "assigned". I thought it means that there's a value associated with m
in the current mvar context. That is, if I used instantiateMVars
on an expression containing m
, then m
would be replaced by something else. How do I check for this notion of being assigned (and what is it called)?
Marcus Rossel (Apr 12 2024 at 09:42):
Mario Carneiro said:
I can't find
assignIfDefeq
though, maybe it's too new (or the PR stalled)?
It's in Std.Tactic.Exact
.
Mario Carneiro (Apr 12 2024 at 09:43):
that is correct
Mario Carneiro (Apr 12 2024 at 09:46):
the issue is that, as the doc says (before making a misleading suggestion), assign
is a low level function, and in particular it has a precondition that the types match
Marcus Rossel (Apr 12 2024 at 09:47):
Mario Carneiro said:
that is correct
Ah, then I guess my wording was just bad. What I mean is that isAssigned
can propagate, and can become true for mvars as a result of other mvars being assigned. Would you say that's correct?
Mario Carneiro (Apr 12 2024 at 09:47):
No, assigning mvars does not cause other mvars to be assigned
Mario Carneiro (Apr 12 2024 at 09:47):
isDefEq
calls can cause mvars to be assigned
Mario Carneiro (Apr 12 2024 at 09:48):
if you do not already know in advance that the types match, you should call isDefEq
before assign
(or use assignIfDefeq
which does both)
Marcus Rossel (Apr 12 2024 at 09:49):
If isDefEq
assigns mvars, why is assign
necessary afterwards? (aside from "The problem is that isDefEq (mkMVar mvarId) x
doesn't assign the mvar at all if it's a proof")
Mario Carneiro (Apr 12 2024 at 09:49):
that's why
Mario Carneiro (Apr 12 2024 at 09:49):
note that I didn't use isDefEq
on the value itself, I used it on the types
Marcus Rossel (Apr 12 2024 at 09:50):
ok, so in a perfect world we never call assign
explicitly?
Mario Carneiro (Apr 12 2024 at 09:50):
in a surprisingly large number of situations you know the types match for structural reasons
Mario Carneiro (Apr 12 2024 at 09:51):
I thought that we would be using assignIfDefeq
more in core but I audited all the uses and most of them can actually safely be assign
Marcus Rossel (Apr 12 2024 at 09:52):
But even then, assign
doesn't do the isAssigned
propagation that I'm looking for. Can I cause this propagation to happen after using assign
?
(I'm also in a situation where I know the types match)
Mario Carneiro (Apr 12 2024 at 09:52):
the types in your example don't match, that's the point
Mario Carneiro (Apr 12 2024 at 09:52):
or at least, they don't match until some variables are assigned
Mario Carneiro (Apr 12 2024 at 09:52):
so that's what isDefEq
does
Mario Carneiro (Apr 12 2024 at 09:53):
if you assign
an mvar before making the types match then you end up in an invalid state
Marcus Rossel (Apr 12 2024 at 09:53):
Ok, I thought "types matching" meant "matching up to mvar assignment" :sweat_smile:
Marcus Rossel (Apr 12 2024 at 09:54):
I think now everything is clear. Thanks for your help!
Last updated: May 02 2025 at 03:31 UTC