2个使用do表示法的类似Haskell函数返回相同的结果,但一个被多次调用

nextState :: IO Int -> IO Int -- 0 1 0 2 0 1 0
nextState stateIO = do
  value <- stateIO
  putStrLn $ "Current state: " ++ show value
  fmap (+1) stateIO

nextState' :: IO Int -> IO Int -- 0 1 2
nextState' stateIO = do
  value <- stateIO
  putStrLn $ "Current state: " ++ show value
  return $ value + 1

main :: IO ()
main = do
  let startStateIO = return 0 :: IO Int
  let states = iterate nextState' startStateIO -- Use nextState or nextState'
  stateInt <- states !! 3
  print stateInt -- 3 in both cases

这个 Haskell 代码有 2 个函数,它们看起来都具有相同的行为。但是,打印调用显示它nextState被调用的次数比nextState'. 我有一个更大的项目,这是一个问题,我无法弄清楚如何转换该函数,以便它被调用的次数最少,因此我无法修复它。

为什么会发生这种情况以及如何在一个不太简单的示例中防止它发生?

请注意,fmap (+1)在我的实际项目中只是一个来自 的函数IO a -> IO a,而不是fmap (a -> a)- 整个事情都在 IO 方面工作,而不是修改内部使用的值(a->a)

回答

这个例子应该更容易理解,它是类似的:

twice :: IO () -> IO ()
twice act = do
   () <- act
   fmap id act -- like what you did in `nextState`

once :: IO () -> IO ()
once act = do
   () <- act
   return $ id ()  -- like what you did in `nextState'`

...或更短

twice :: IO () -> IO ()
twice act = act >> act

once :: IO () -> IO ()
once act = act

例如,

> twice (putStrLn "hello")
hello
hello
> once (putStrLn "hello")
hello

迭代once不做任何事情,因为它只是身份。

> iterate once (putStrLn "hello") !! 4
hello

迭代两次,但是...

Prelude> iterate twice (putStrLn "hello") !! 4
hello
hello
hello
hello
hello
hello
hello
hello
hello
hello
hello
hello
hello
hello
hello
hello


以上是2个使用do表示法的类似Haskell函数返回相同的结果,但一个被多次调用的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>