Rebinding is nicer than mutation, but neither are referentially transparent.
counter = 0
counter = counter + 1
This is very different to shadowing where there is a clear scope to the rebinding. In this case, I cannot employ equational reasoning within a scope but must instead trace back through every intervening statement in the scope to check whether the variable is rebound. counter = 0
...
counter = counter + 1
...
vs let counter = 0 in
...
let counter = counter + 1 in
...No, it’s rebinding the variable within the same scope. Even if you view it as implicitly creating a new scope, the implicit part means it can happen on any line so you have to scan them all and mentally keep track of what changes where, (almost) exactly as you would for imperative code.