Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 用IO包装的惰性列表_Haskell_Lazy Evaluation_Io Monad - Fatal编程技术网

Haskell 用IO包装的惰性列表

Haskell 用IO包装的惰性列表,haskell,lazy-evaluation,io-monad,Haskell,Lazy Evaluation,Io Monad,假设代码 f :: IO [Int] f = f >>= return . (0 :) g :: IO [Int] g = f >>= return . take 3 在ghci中运行g时,会导致堆栈溢出。但我在想,也许可以懒散地对它进行评估,并生成[0,0,0]包装在IO中。我怀疑这里应该归咎于IO,但我真的不知道。显然,以下工作: f' :: [Int] f' = 0 : f' g' :: [Int] g' = take 3 f' 编辑:事实上,我对拥有如此

假设代码

f :: IO [Int]
f = f >>= return . (0 :)

g :: IO [Int]
g = f >>= return . take 3
在ghci中运行
g
时,会导致堆栈溢出。但我在想,也许可以懒散地对它进行评估,并生成
[0,0,0]
包装在
IO
中。我怀疑这里应该归咎于
IO
,但我真的不知道。显然,以下工作:

f' :: [Int]
f' = 0 : f'

g' :: [Int]
g' = take 3 f'

编辑:事实上,我对拥有如此简单的函数
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
h::a->IO[b或c]
HA=do
(r,a')>=返回。(十)
y@(右)->返回[y]
h'::IO(b c,a中的任意一个)
--不平凡的事
main::IO()

main=mapM_uu打印。假设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

有关使用
MonadFix
的更有趣的示例,请参阅。

我不确定这是否是一个合适的用法,但是
unsafeInterleaveIO
将延迟
f
的IO操作,直到请求
f
中的值,从而获得您想要的行为:

module Tmp where
import System.IO.Unsafe (unsafeInterleaveIO)

f :: IO [Int]
f = unsafeInterleaveIO f >>= return . (0 :)

g :: IO [Int]
g = f >>= return . take 3

*Tmp> g
[0,0,0]

听起来您想要一个混合了列表和
IO
功能的monad。幸运的是,这正是我们的目的。这是你的例子,用一个
h'
计算Collatz序列,并询问用户对序列中每个元素的感觉(我真的想不出任何符合你轮廓形状的东西)


我想现代的方法可能会使用管道、导管、迭代器或其他东西,但我对它们的了解还不够,无法谈论与
ListT

相比的折衷。我可能过于简单化了,我会编辑原始帖子。这似乎可以做到,谢谢。使用这个名称有什么缺点吗?名称不太令人放心;)。这就是为什么我说我不确定这是否合适。不愉快的交流有时是可以的,但总的来说是危险的;找一个比我更有经验的人来告诉你这是否是一个好主意。@Jakubdiel缺点是它会打乱你关于
IO
如何发生的直觉:通常,在
m>=f
中,我们期望
m
IO
发生在
f
应用于参数之前;但是在
unsafeInterleaveIO m>=f
中,
m
IO
将被延迟。将此转换为您的问题上下文,这意味着
h
列表的使用者决定执行
h'
的哪些迭代(如果有!)。编译器优化也很脆弱。因此,如果你在做一些你真正关心发生的
IO
的事情——这几乎总是——这是一个非常糟糕的主意!
h'
是否调用
h
?你能告诉我们真正的代码吗?你想让它只执行足够的
IO
来生成所需的结果,还是不管它是否都要执行?只是为了生成所需的结果,而实际的代码要大得多,更混乱,我仍然相信我在粘贴简化版本时会帮到它
h
不是从
h'
调用的,我犯了错误,我应该考虑一下。啊,好吧,如果你想让求值驱动执行,
unsafeInterleaveIO
是你唯一的选择。这似乎是一个非常简单的方法,但是这个库有点讨厌——为什么它不导出
ListT
构造函数呢?有没有可能有一天这个
ListT
会取代
transformers
中的一个呢?我能不能懒洋洋地把
ListT IO(或者a b)
转换成
IO[或者a b]
这样我就不必用列表的东西来说服打电话的人了?我本以为
toList
是懒惰的,但它似乎不是:/我还想选择是使用
take 3
还是仅仅使用identity,所以我认为流可能是无限的。@jakubdanial在
main
的上述定义中,你当然可以用
id
代替
take 3
,没有问题。诚然,
toList
在懒惰IO的意义上并不是懒惰的——基于我在另一个答案中概述的所有原因,您应该将其视为一件好事。在调用
toList
之前,您需要决定是要调用
take 3
还是
id
。然而,
toList
确实是将
ListT IO(a b)
转换为
IO[a b]
的正确方法。是的,但如果我选择
id
则当函数未终止时,严格性会导致我无法读取中间结果:(这超出了使用
ListT
而不是在我对原始问题的编辑中使用普通列表的目的。通常情况下,函数不会终止。
module Tmp where
import System.IO.Unsafe (unsafeInterleaveIO)

f :: IO [Int]
f = unsafeInterleaveIO f >>= return . (0 :)

g :: IO [Int]
g = f >>= return . take 3

*Tmp> g
[0,0,0]
import Control.Monad.IO.Class
import qualified ListT as L

h :: Int -> L.ListT IO (Either String ())
h a = do
  (r, a') <- liftIO (h' a)
  case r of
    x@(Left  _) -> L.cons x (h a')
    y@(Right _) -> return y

h' :: Int -> IO (Either String (), Int)
h' 1 = return (Right (), 1)
h' n = do
  putStrLn $ "Say something about " ++ show n
  s <- getLine
  return (Left s, if even n then n `div` 2 else 3*n + 1)

main = readLn >>= L.traverse_ print . L.take 3 . h
> main
2
Say something about 2
small
Left "small"
Right ()
> main
3
Say something about 3
prime
Left "prime"
Say something about 10
not prime
Left "not prime"
Say something about 5
fiver
Left "fiver"