如何在使用IO时在Haskell中的两个函数调用之间共享IORef状态?

时间:2017-12-08 08:06:55

标签: haskell shared-state ioref

我正在努力学习Haskell,我正在玩IORef,我试图保存并查找记录。我的code看起来像这样(注意我在这个例子中选择了“String”作为IORef类型只是为了方便和简介,在我的实际代码中我正在使用记录。而且我也忽略了我使用Set而不是Map,我会改变那个):

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"

我想要实现的是共享两个函数调用之间的状态(Set),以便findStringInState可以返回刚刚插入Set中的String。但是当我运行doStuff函数时,我得到了这个:

*MyTest> doStuff
### saved: fromList ["string1"]
Looking for string1 in: fromList []
Nothing

我可能误解了一些事情,因为我认为IORef确实应该是我州的容器。

  1. 为什么这不起作用?
  2. 我该怎么做才能让它发挥作用?

1 个答案:

答案 0 :(得分:6)

似乎您将IO IORefStateIORefState(没有IO)混为一谈,更常见的是,IO aa混淆。

在您的情况下,IO IORefState的值操作 newIORef empty,表示“从头开始创建全新IORef的操作”。
相比之下,IORefState(没有IO)是正确的原始对象,您应该在使用它的函数之间共享它(saveStringToState和{{1 }})。
然后,findStringInStatesaveStringToState分别调用findStringInState,即每个newIORef empty创建一个不同的IORefState对象,不会受到另一个的影响。

要解决此问题,您必须在newIORef empty函数中调用IO(使用<-} doStuff操作)并共享由{{1}创建的IORefState而不是newIORef empty

IO IORefState

在我看来, saveStringToState :: IORefState -> String -> IO String ... findStringInState :: IORefState -> String -> IO (Maybe String) ... let stateIO = newIORef empty in do ioRef <- stateIO saveStringToState ioRef "string1" findStringInState ioRef "string1" -- Or, more simply: do ioRef <- newIORef empty saveStringToState ioRef "string1" findStringInState ioRef "string1" IO a之间的差异类似于“返回键入a的值的函数对象(带有一些副作用)”和“只是在其他编程语言中输入a”的原始值。