2

Is it possible to reassign the same variable in ghci ? Of course this isn't possible in normal Haskell (at least without something like an IORef), but I was curious because the it variable in ghci that you get by enabling the :m option. With that enabled I get an experience where it is effectively reassignable by not binding the next expression to anything, e.g.

λ> 1 + 1
2
it :: Num a => a
λ> it
2
it :: Num a => a
λ> it + 1
3
it :: Num a => a
λ> it
3
it :: Num a => a

Is there a way to realize the same effect for an arbitrary named variable, not just the special it ?

  • 3
    No it is not reassigning, you simply define a *second* more locally scoped variable `it`. – Willem Van Onsem Apr 13 '20 at 13:45
  • 1
    See also [What is immutable data?](https://stackoverflow.com/q/44406434/791604). – Daniel Wagner Apr 13 '20 at 15:49
  • Under ghci, the expression `x <- return (x+1)` seems to do what you would expect. – jpmarinier Apr 13 '20 at 18:29
  • 1
    @jpmarinier but only *seems to*. :) it's still shadowing, just in one line of code instead of two, as seen e.g. here: [how to increment a variable in functional programming](https://stackoverflow.com/a/11923567/849891). – Will Ness Apr 13 '20 at 19:38

1 Answers1

5

Each new line in GHCi enters new, nested scope. Consider

> x = 1
> x
1
> foo y = x + y
> foo 41
42
> x = 7                 -- new x
> x
7
> bar y = x + y         -- refers to new x
> bar 41
48
> foo 41                -- still refers to the same old x binding
42

Second binding of x defines new same-named variable.

Naturally, it shadows the previous binding for that name, making it inaccessible in itself. Any other entity (like foo) which referenced it before though, will continue to hold that reference, no matter the shadowing for the newer, nested environments.

This follows the principle of immutability of values in Haskell: the foo remains the same.

One caveat:

> :{
  x = 1
  foo y = x + y
  x = 7                   -- error: conflicting definition
  bar y = x + y
  :}

causes an error, because lines entered in multiline mode belong to the same scope.

In other words the answer to your question is no, it would break the fundamental purity properties of Haskell.

it is not reassigned either, it is defined anew in each new nested environment, whenever there is any output, being bound to the output value:

> x
7
> it
7
> it_2                       -- made a typo

error: Not in scope: `it_2'
> it+2                       -- no previous output, same `it` in effect
9
> it+2                       -- most recent `it` is 9
11

So you can always shadow your variable and create a new binding with the same name, but you can't change the old binding, because it is a part of history now, and the past can not be changed, by definition. It already happened.

Or you could create your own Haskell interpreter where you'd give a user an ability to "change" an old binding, but what would really happen is that a new copy of the interpreter would be spawned even "playing" all the recorded user actions at the prompt -- new definitions and all -- from that point on. Then the user would end up with a changed copy which would be oblivious to the existence of the first one. You could even kill the original at that point. Could this be considered as user having changed the past? The user's past hasn't changed, their time goes forward. The new copy's past was always what it is now, as far as it is concerned. The older original (copy?) is gone... A question to ponder.

Will Ness
  • 70,110
  • 9
  • 98
  • 181