【问题标题】:How to share IORef state between two function invocations in Haskell when using IO?使用 IO 时如何在 Haskell 中的两个函数调用之间共享 IORef 状态?
【发布时间】:2018-05-22 10:47:15
【问题描述】:

我正在尝试学习 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 确实应该是我的状态的容器。

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

【问题讨论】:

    标签: haskell shared-state ioref


    【解决方案1】:

    您似乎将IO IORefStateIORefState(没有IO)混淆了,更一般地说,IO aa 混淆了。

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

    要解决此问题,您必须在doStuff 函数中调用newIORef empty(作为使用&lt;-IO 操作)并共享由newIORef empty 创建的IORefState 而不是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 的值的函数对象(有一些副作用)”和“只是键入的原始值”之间的区别在其他编程语言中为 a"。

    【讨论】:

      猜你喜欢
      • 2020-06-23
      • 1970-01-01
      • 1970-01-01
      • 2019-09-19
      • 2019-12-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-02-29
      相关资源
      最近更新 更多