I'm trying to learn Haskell and I'm playing around with IORef to which I try to save and find records. My code looks something like this (note that I've chosen "String" as IORef type in this example only for convienence and briefty, in my actual code I'm using a record. And also ignore that I'm using a Set instead of a Map, I will change that):
module MyTest where
import Data.IORef
import Data.Set
import Data.Foldable (find)
type State = (Set String)
type IORefState = IORef State
saveStringToState :: IO IORefState -> String -> IO String
saveStringToState stateIO string = do
state <- stateIO
atomicModifyIORef
state
(\oldStrings ->
let updatedStrings = insert string oldStrings
in (updatedStrings, updatedStrings))
stringsState <- readIORef state :: IO State
putStrLn ("### saved: " ++ show stringsState)
return string
findStringInState :: IO IORefState -> String -> IO (Maybe String)
findStringInState stateIO soughtString = do
state <- stateIO :: IO IORefState
strings <- readIORef state :: IO State
putStrLn ("Looking for " ++ soughtString ++ " in: " ++ show strings)
return $ find (== soughtString) strings
doStuff =
let stateIO = newIORef empty
in do saveStringToState stateIO "string1"
findStringInState stateIO "string1"
What I want to achieve is to share the state (Set) between the two function calls so that findStringInState
can return the String
that I just inserted into the Set. But when I run the doStuff
function I get this:
*MyTest> doStuff
### saved: fromList ["string1"]
Looking for string1 in: fromList []
Nothing
I've probably misunderstood something since I thought the IORef should indeed be the container for my state.
- Why is this not working?
- What can I do to make it work?
Seems that you confused
IO IORefState
withIORefState
(withoutIO
), more generally,IO a
witha
.In your case, the value of
IO IORefState
is the actionnewIORef empty
, which represents "an action that creates a fresh newIORef
from scratch".By contrast,
IORefState
(withoutIO
) is the right, raw object that you should share among the functions that use it (saveStringToState
, andfindStringInState
).Then, both
saveStringToState
andfindStringInState
separately callnewIORef empty
, i.e. each of them create a differentIORefState
object, which cannot be affected by the other.To fix, you must call
newIORef empty
(as anIO
action using<-
) indoStuff
function and share theIORefState
created bynewIORef empty
instead ofIO IORefState
:In my opinion, the difference between
IO a
witha
is similar to the difference between "a function object that returns a value typed asa
(with some side effects)" and "just a raw value typed asa
" in the other programming languages.