我正在尝试学习Haskell,并且正在尝试使用IORef来保存和查找记录。我的code看起来像这样(请注意,在本示例中,出于方便和简明的考虑,我已选择“ String”作为IORef类型,在我的实际代码中,我正在使用记录。也忽略了我正在使用设置而不是地图,我将其更改):

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应该确实是我所在州的容器以来,我可能误解了一些东西。


为什么这不起作用?
我该怎么做才能使其正常工作?

最佳答案

似乎您将IO IORefStateIORefState(没有IO)混淆了,更普遍的是,将IO aa混淆了。

在您的情况下,IO IORefState的值是操作newIORef empty,它表示“从头开始创建新的新IORef的操作”。
相比之下,IORefState(没有IO)是您应该在使用它的功能(saveStringToStatefindStringInState)之间共享的正确的原始对象。
然后,saveStringToStatefindStringInState分别调用newIORef empty,即它们各自创建一个不同的IORefState对象,而彼此不受此对象的影响。

要解决此问题,必须在newIORef empty函数中调用IO(作为使用<-doStuff动作)并共享由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 aa的区别类似于“返回一个键入为a的值(具有某些副作用)的函数对象”和“只是一个键入为”在其他编程语言中。

07-24 09:48