4

In GHCI (version 9.0.1), the following gives back what I would expect:

ghci> import Data.IORef
ghci> ref <- newIORef ([] :: [Int])
ghci> modifyIORef ref (1:)
ghci> readIORef ref
[1]

But when I try the same thing this way:

ghci> import Data.IORef
ghci> ref = newIORef ([] :: [Int])
ghci> ref >>= \r -> modifyIORef r (1:) 
ghci> ref >>= readIORef
[]

An empty list is returned, as if the modify never happened. Why does this happen? Shouldn't the output be the same?

duplode
  • 33,731
  • 7
  • 79
  • 150
Lukas
  • 43
  • 3
  • 1
    You might also find the desugaring of these enlightening. The first is `m >>= (\x -> f x >> g x)`; the latter is `(m >>= (\x -> f x)) >> (m >>= (\x -> g x))`. Notice how `m` appears once and each `x` appears twice in the first one, but `m` appears twice and each `x` appears just once in the second one. I think part of what's confusing about your presentation is that in your first example, your `ref` corresponds to `x`, but in your second example, your `ref` corresponds to `m`, so you picked the same name for two different things. – Daniel Wagner Dec 17 '21 at 15:45
  • 3
    The issue is strongly related to https://stackoverflow.com/questions/28624408/equal-vs-left-arrow-symbols-in-haskell/28625714#28625714 Remember that `ref = newIORef []` means that you can always replace `ref` with `newIORef []`, so the behavior of the last lines is equivalent to `newIORef [] >>= ...`. – chi Dec 17 '21 at 16:46

1 Answers1

9

When you write ref = newIORef ..., the type of ref is IO (IORef [Int]) (because that is the type that newIORef returns). Every time you execute that IO action (by including it in an IO action that GHCI evaluates), you get a new IORef.

In contrast, when you write ref <- newIORef, you are asking GHCI to execute the IO action, yielding an IORef [Int], and save the resulting IORef to the variable ref. Then all the later operations you perform on it are performed on the same IORef.

amalloy
  • 89,153
  • 8
  • 140
  • 205