`mfix`没有按预期工作

时间:2014-10-29 16:08:39

标签: haskell monads

我希望以下代码首先提示start:,然后等待用户响应,然后回复之前的用户响应,然后等待新的响应:

import System.IO (hFlush, stdout)
import Control.Monad.Fix (mfix)

f :: [String] -> IO [String]
f = mapM $ \x -> putStr x >> putStr ": " >> hFlush stdout >> getLine

g x = f ("start":x)

main = mfix g

但是在输入第一行之后它会给出错误thread blocked indefinitely in an MVar operation

为什么会这样,我该如何解决它(原谅双关语)?

2 个答案:

答案 0 :(得分:7)

这不能起作用的原因是mfix ff中只运行一次效果。这遵循紧缩规则

mfix (\x -> a >>= \y -> f x y)  =  a >>= \y -> mfix (\x -> f x y)

特别是

mfix (\x -> a >> f x)  =  a >> mfix f

对于MonadFix的任何正确实例。因此,仅针对monadic动作中的纯(延迟计算)值计算固定点,对于效果,。在您的情况下,使用mfix要求只输入一次打印/读取字符,使输入等于输出,这是不可能的。这不是mfix的正确用例。您可以使用mfixIO一起构建IO中的循环数据结构,例如these examples

在您的情况下,您应该使用iterateM_或类似的内容,而不是mfix。另见iterate + forever = iterateM? Repeating an action with feedback

答案 1 :(得分:4)

不幸的是,mfix monad中的IO并没有像这样零碎地生成列表。这是因为IO monad中的大多数操作都非常严格:在完成整个操作之前,他们不会生成任何部分的结果。特别是,mapM中的IO将不会返回其结果列表的任何部分,直到它通过所有输入列表,这使得mfix无法将结结合在一起。正确的方式在这里。

一般情况下,mfix中的IO实际上只有在整个mfix操作完成后才严格查看并列值才有效。这仍然具有一些可能的用途,例如仅使用newIORef初始化具有可变单元循环的数据结构。