jak*_*iel 3 haskell lazy-evaluation io-monad
假设代码
f :: IO [Int]
f = f >>= return . (0 :)
g :: IO [Int]
g = f >>= return . take 3
Run Code Online (Sandbox Code Playgroud)
当我g
在ghci中运行时,它会导致stackoverflow.但我想也许它可以懒惰地评估并生产[0, 0, 0]
包装IO
.我怀疑这IO
是责备,但我真的不知道.显然以下工作:
f' :: [Int]
f' = 0 : f'
g' :: [Int]
g' = take 3 f'
Run Code Online (Sandbox Code Playgroud)
编辑:事实上我对拥有这么简单的功能不感兴趣f
,原始代码看起来更像是这样:
h :: a -> IO [Either b c]
h a = do
(r, a') <- h' a
case r of
x@(Left _) -> h a' >>= return . (x :)
y@(Right _) -> return [y]
h' :: IO (Either b c, a)
-- something non trivial
main :: IO ()
main = mapM_ print . take 3 =<< h a
Run Code Online (Sandbox Code Playgroud)
h
进行一些IO
计算并Left
在列表中存储invalid()响应,直到产生有效的response(Right
).尝试是懒惰地构建列表,即使我们在IO
monad中.因此,读取结果的人h
甚至可以在完成之前开始使用列表(因为它甚至可能是无限的).如果读取结果的人3
无论如何都只关心第一个条目,那么甚至不必构建列表的其余部分.我感觉这是不可能的:/.
是的,IO
应该归咎于此.>>=
因为IO
在"世界状况"中是严格的.如果你写m >>= h
,你会得到一个首先执行动作的动作m
,然后应用于h
结果,最后执行动作h
产生.你的f
行为不"做任何事" 并不重要; 它无论如何都必须执行.因此,你最终会在无限循环中f
反复开始动作.
值得庆幸的是,有一种解决方法,因为IO
是一个实例MonadFix
.您可以"神奇地" IO
从该操作中访问操作的结果.重要的是,访问必须足够懒惰,否则你会陷入无限循环.
import Control.Monad.Fix
import Data.Functor ((<$>))
f :: IO [Int]
f = mfix (\xs -> return (0 : xs))
-- This `g` is just like yours, but prettier IMO
g :: IO [Int]
g = take 3 <$> f
Run Code Online (Sandbox Code Playgroud)
甚至还有这样让你使用一点在GHC语法糖do
符号与rec
关键字或mdo
符号.
{-# LANGUAGE RecursiveDo #-}
f' :: IO [Int]
f' = do
rec res <- (0:) <$> (return res :: IO [Int])
return res
f'' :: IO [Int]
f'' = mdo
res <- f'
return (0 : res)
Run Code Online (Sandbox Code Playgroud)
有关使用方法的更有趣示例MonadFix
,请参阅Haskell Wiki.
归档时间: |
|
查看次数: |
305 次 |
最近记录: |