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

Joh*_*han 2 haskell shared-state ioref

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

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"
Run Code Online (Sandbox Code Playgroud)

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

*MyTest> doStuff
### saved: fromList ["string1"]
Looking for string1 in: fromList []
Nothing
Run Code Online (Sandbox Code Playgroud)

我可能误解了一些东西,因为我认为IORef确实应该是我州的容器.

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

Yuj*_*oto 6

似乎你IO IORefStateIORefState(没有IO)混淆,更普遍的是,IO aa.

在您的情况下,值的作用IO IORefState动作 newIORef empty,它表示" IORef从头开始创建新动作的动作".
相比之下,IORefState(没有IO)是您应该在使用它的函数(,和)之间共享的正确的原始对象. 然后,这两个和单独呼叫,即,它们中的每一个创建不同对象,它不能由其他的影响.saveStringToStatefindStringInState
saveStringToStatefindStringInStatenewIORef emptyIORefState

要修复,您必须在函数中调用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"
Run Code Online (Sandbox Code Playgroud)

在我看来,IO awith 之间的区别a类似于"一个函数对象返回一个键入的值a(带有一些副作用)"和"只是一个原始值类型为a"在其他编程语言中的区别.