假设我们想要一组按需计算的值(计算有一些副作用),并且在您再次访问它们时不再重新计算。
以下两种幼稚的方法是行不通的:
Prelude> let f x = print x >> return x
Prelude> let a = map f "abc"
Prelude> :t a
a :: [IO Char]
Prelude> head a
'a'
'a'
Prelude> head a
'a'
'a'
Run Code Online (Sandbox Code Playgroud)
Prelude> let b = mapM f "abc"
Prelude> :t b
b :: IO [Char]
Prelude> fmap head b
'a'
'b'
'c'
'a'
Prelude> fmap head b
'a'
'b'
'c'
'a'
Run Code Online (Sandbox Code Playgroud)
我该如何正确地做到这一点?到目前为止,我什至不明白集合应该有什么类型。
这是一个使用IORef. cache下面的函数将 anIO a转换为另一个IO a只会执行一次副作用,并在下次执行时返回缓存的值。
import Data.IORef
cache :: IO a -> IO (IO a)
cache action = do
v <- newIORef Nothing
return $ do
x <- readIORef v
case x of
Nothing -> do
res <- action
writeIORef v $ Just res
return res
Just res -> return res
Run Code Online (Sandbox Code Playgroud)
这是你的功能:
f :: Char -> IO Char
f x = print x >> return x
Run Code Online (Sandbox Code Playgroud)
这是一个展示如何使用cache.
main :: IO ()
main = do
acts <- mapM (cache . f) "abc"
-- here we have acts :: [IO Char]
putStrLn "1st: "
head acts -- prints 'a'
putStrLn "2nd: "
head acts -- prints nothing
putStrLn "3rd: "
head acts -- prints nothing
return ()
Run Code Online (Sandbox Code Playgroud)
只有第一个调用会打印'a'在屏幕上。