Как разделить состояние IORef между двумя вызовами функций в Haskell при использовании IO?
Я пытаюсь изучить 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
что я только что вставил в набор. Но когда я бегу doStuff
Функция я получаю это:
*MyTest> doStuff
### saved: fromList ["string1"]
Looking for string1 in: fromList []
Nothing
Возможно, я что-то неправильно понял, так как думал, что IORef действительно должен быть контейнером для моего состояния.
- Почему это не работает?
- Что я могу сделать, чтобы это работало?
1 ответ
Кажется что ты запутался IO IORefState
с IORefState
(без IO
), в более общем смысле, IO a
с a
,
В вашем случае значение IO IORefState
это действие newIORef empty
, которое представляет собой "действие, которое создает свежий новый IORef
с нуля".
В отличие от IORefState
(без IO
) является правильным, необработанным объектом, который вы должны разделить между функциями, которые его используют (saveStringToState
, а также findStringInState
).
Тогда оба saveStringToState
а также findStringInState
отдельно звонить 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 a
с a
похоже на разницу между "функциональным объектом, который возвращает значение, набранное как a
(с некоторыми побочными эффектами)"и" просто необработанное значение, напечатанное как a
"на других языках программирования.