2

我正在尝试学习 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"

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

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

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

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

1 回答 1

6

似乎您IO IORefStateIORefState(without IO) 混淆了,更一般地说,IO aa.

在您的情况下, 的值IO IORefStateaction ,它表示“从头开始newIORef empty创建全新的操作”。 相比之下,(without ) 是正确的原始对象,您应该在使用它的函数 (和) 之间共享它。 然后,两者分别调用,即它们中的每一个都创建一个不同的对象,该对象不会受到另一个对象的影响。IORef
IORefStateIOsaveStringToStatefindStringInState
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"

在我看来,with 之间IO aa区别类似于其他编程语言中“返回类型为a(具有一些副作用)的值的函数对象”和“只是类型为”的原始值之间的区别。a

于 2017-12-08T08:45:16.453 回答