在 Haskell 中仅在计算期间执行一次副作用

zab*_*kar 6 haskell

假设我们想要一组按需计算的值(计算有一些副作用),并且在您再次访问它们时不再重新计算。

以下两种幼稚的方法是行不通的:

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)

我该如何正确地做到这一点?到目前为止,我什至不明白集合应该有什么类型。

chi*_*chi 5

这是一个使用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'在屏幕上。