Search code examples
haskellfunctional-programmingimmutabilityghci

What does immutable variable in Haskell mean?


I am quite confused with the concept of immutable variables in Haskell. It seems like that we can't change the value of variables in Haskell. But when I tried following code in GHCI, it seemed like the value of variables did change:

Prelude> foo x=x+1
Prelude> a=1
Prelude> a
1
Prelude> foo a
2
Prelude> a=2
Prelude> a
2
Prelude> foo a
3

Does this conflict with the idea of immutable variables?

Many thanks!


Solution

  • Haskell doesn't allow you to modify existing variables. It does, however, allow you to re-use variable names, and that's all that's happening here. One way to see this is to ask GHCi, using the :i[nfo] directive, where the variable was declared:

    Prelude> let a = 1
    Prelude> :i a
    a :: Num a => a     -- Defined at <interactive>:2:5
    Prelude> let a = 2
    Prelude> :i a
    a :: Num a => a     -- Defined at <interactive>:4:5
    

    These are actually two entire seperate, different variables, which just happen to be called the same name! If you just ask for a, the newer definition will be “preferred”, but the old one is still there – one way to see this, as remarked by chi in the comments, is to use a in a function:

    Prelude> let a = 2
    Prelude> :i a
    a :: Num a => a     -- Defined at <interactive>:4:5
    Prelude> let f x = a + x
    Prelude> let a = 3
    Prelude> f (-2)
    0
    

    f never needs to care that you've defined a new variable that's also called a; from its perspective a was one immutable variable that always stays as it is.


    It's worth talking a bit about why GHCi prefers the later definition. This does not otherwise happen in Haskell code; in particular if you try to compile the following module, it simply gives an error concerning duplicate definition:

    a = 1
    a = 2
    
    main :: IO ()
    main = print a
    

    The reason that something like this is allowed in GHCi is that it works different from Haskell modules. The sequence of GHCi commands forms in fact a sequence of actions in the IO monad; i.e. the program would have to be

    main :: IO ()
    main = do
       let a = 1
       let a = 2
       print a
    

    Now, if you've learned about monads you'll know that this is just syntactic sugar for

    main =
       let a = 1 in (let a = 2 in (print a))
    

    and this is really the crucial bit for why you can re-use the name a: the second one, a = 2, lives in a narrower scope than the first. So it is more local, and local definitions have priority. Whether this is a good idea is a bit debatable; a good argument for it is that you can have a function like

    greet :: String -> IO ()
    greet name = putStrLn $ "Hello, "++name++"!"
    

    and it won't stop working just because somebody defines elsewhere

    name :: Car -> String
    name car | rollsOverAtRightTurn car   = "Reliant Robin"
             | fuelConsumption car > 50*litrePer100km
                                          = "Hummer"
             | ...                        = ...
    

    Besides, it's really quite useful that you can “redefine” variables while fooling around in GHCi, even though it's not such a good idea to redefine stuff in a proper program, which is supposed to show consistent behaviour.


    As dfeuer remarks, this is not the whole truth. You can do some things in GHCi that aren't allowed in an IO do-block, in particular you can define data types and classes. But any normal statement or variable definition act as it were in the IO monad.