2

What exactly does this code do? Is someMap a copy of the object (of ::Data.Map.Strict.Map) referred to by myMap or it's a reference only? I mean can someMap change (by another thread) after I read it with readIORef? Something like C's volatile... Is it possible? I expect that it's copy/snapshot, so any changes will not affect my someMap, or ...?

     do
        ....
        someMap <- readIORef myMap
        ....
Will Ness
  • 70,110
  • 9
  • 98
  • 181
RandomB
  • 3,367
  • 19
  • 30

2 Answers2

5

No it is not a copy. In Haskell there is no such thing as a "copy", there are only values and all values are immutable.

An IORef contains a value. The IORef itself is mutable: you can change the value that it contains. The value itself is not mutable. To understand this, think of an IORef Int which currently contains 5. If you get that value out and add one to it to get 6 you have created a new value, but you have not changed the 5 value to have suddenly become 6, because the value of 5 is immutable.

Likewise if I create a Map with the value fromList [("foo", 5), ("bar, 6")] and put it in an IORef, the IORef now contains that value, but the value itself is immutable. If I get the value out and add a new entry using Map.insert I have created a new value, not modified the original value, exactly the same way as with the 5 and 6 above.

Hopefully that answers your question. However you may now have another one. If all values are immutable, how can IORefs change?

The answer is that the IORef itself does not change. However IORefs exist as a kind of portal to the mutable ever-changing chaos that we call "The Real World". In The Real World you can do the same thing twice and get different results. That includes readLine and readIORef. The IO monad exists to quarantine this chaos while still allowing the program to interact with it. Hence every function that works with IORefs is in the IO monad.

Paul Johnson
  • 17,438
  • 3
  • 42
  • 59
  • yes, of course it is so, but the question is: if my thread A read the map with `readIORef` and I am doing something with it, can it be changed by another thread B? Haskell as I know does copy-on-write, also IO monad is only wrapper for good old C-like code (readIORef/writeIORef are even reorderable like in C), so: can `someMap` be changed by another thread after I read it? – RandomB Aug 14 '19 at 07:47
  • 2
    @Paul-AG: no, since if you use `writeIORef`, you write a different value to the reference. Datastructures are never modified, you create a "shallow" modified copy. So if you write to the `IORef`, then it means that you let the ref now point to a different map. – Willem Van Onsem Aug 14 '19 at 08:05
  • 3
    @Paul-AG I think you are trying to understand the semantics of Haskell by understanding how it is translated into low-level operations on memory. This works for imperative languages like C or C++, or even Java, because the underlying programmer model for those languages is a von Neumann architecture. It does not work for Haskell because the underlying programmer model for Haskell is the lambda calculus. Therefore you should stop trying to think of Haskell semantics in terms of pointers and copies and try instead to think in terms of values and functions. – Paul Johnson Aug 14 '19 at 09:12
  • 4
    Although this particular case is very easy to explain by analogy to pointers and copies. An IORef I'd like a mutable variable containing a pointer to an immutable object. Reading a pointer is atomic, so it's not possible for another thread to write to the pointer "while" I'm reading it. Once I've read it to find out what object is pointed to, it doesn't matter what other threads do; they can't change the immutable object I'm now working with, so they can only affect me if I read the pointer again. – Ben Aug 14 '19 at 14:07
2

readIORef :: IORef a -> IO a, so myMap must be IORef a and readIORef myMap :: IO a.

And so someMap :: a, because it's to the left of <- in the do code line of the type IO a (it's a <- M a, always, in do notation).

In your case, that a ~ Data.Map.Strict.Map k v, i.e. a pure immutable value.

If another thread writes some new value into that myMap :: IORef (Data.Map.Strict.Map k v), then, it does. But it won't change the pure value that was already drawn from it before the switch-up.

Effectful code has time. Pure referentially transparent code with immutable data is timeless.

(What is true, is true, regardless of how long it takes to prove it.)

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