Zulip Chat Archive
Stream: lean4
Topic: Cannot mutate mutable variable
Zach Battleman (Jun 24 2023 at 17:49):
Hi! I was working on a little project and I encountered some strange behavior. Here is a minimal working example
def baz (bar : Nat → IO PUnit) := fun x => bar x
def foo : IO Nat := do
let mut myVar := 0
baz (fun x => do
myVar := myVar + 10
IO.println s!"{myVar}")
return 0
The error I get is that myVar
needs to be declared mutably in order to mutate it, but it clearly is. Is this a result of myVar
being fixed when I enter the body of baz
? A similar example of nested dos works fine:
def foo' : IO Nat := do
let mut myVar := 0
do
myVar := myVar + 10
IO.println s!"{myVar}"
return 0
I guess I'm just wondering if the top example is intended behavior (excluding the error message which seems confusing) and if so specifically why? Thanks so much!
Kyle Miller (Jun 24 2023 at 17:54):
Basically, mutable variables are only mutable in when you have nested do/if/for/unless expressions, but when you have a do
inside a fun
then it's not considered to be nested anymore. It might help remembering that the variables are just "mutable" -- the do
notation is figuring out how to rewrite your code into functional programming equivalents to pass explicit state around. When you're passing that function to baz
, it doesn't have the freedom to change that function so that this state is passed in and returned.
Kyle Miller (Jun 24 2023 at 17:56):
In the first example, Lean is seeing that you're trying to mutate myVar
in a context where myVar
isn't declared as a mutable variable (due to the fun
separating this do
from the outer do
, so from that perspective it's the right error message, but it could probably be improved.
Kyle Miller (Jun 24 2023 at 17:58):
In the second example, directly nested do
notation is handled specially so it's able see myVar
as still being mutable.
Zach Battleman (Jun 24 2023 at 17:58):
Ah I see, that makes sense, thanks!
James Gallicchio (Jun 24 2023 at 22:05):
I'm actually sort of surprised by the error message here -- I would've expected it to say that myVar
is a constant defined outside the scope of the do
block. It seems useful to differentiate this error from something like
def foo : IO Nat := do
let myVar := 0
myVar := 1
sorry
where the "only variables declared using let mut
can be mutated" error message makes more sense
James Gallicchio (Jun 24 2023 at 22:08):
I find it even more confusing in a situation like this:
def bar := 0
def foo : IO Nat := do
bar := 1
return 0
/-
`bar` cannot be mutated, only variables declared using
`let mut` can be mutated. If you did not intent to mutate
but define `bar`, consider using `let bar` instead
-/
But I'm not sure whether that information (is a constant defined in the current do-block?) is available to the do macro.
Last updated: Dec 20 2023 at 11:08 UTC