我希望以下代码首先提示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
Run Code Online (Sandbox Code Playgroud)
但它thread blocked indefinitely in an MVar operation在输入第一行后给出错误.
为什么这个以及如何解决它(请原谅双关语)?
这不能起作用的原因是mfix f在f一次运行任何效果.这是紧缩政策的结果
mfix (\x -> a >>= \y -> f x y) = a >>= \y -> mfix (\x -> f x y)
Run Code Online (Sandbox Code Playgroud)
特别是
mfix (\x -> a >> f x) = a >> mfix f
Run Code Online (Sandbox Code Playgroud)
对于任何正确的实例MonadFix.因此,固定点仅针对monadic动作中的纯(延迟计算)值计算,而不是针对效果计算.在您的情况下,使用mfix请求只打印/读取字符一次,使输入等于输出,这是不可能的.这不是一个正确的用例mfix.你会使用mfix与IO例如以构建循环数据结构IO像这些例子.
在你的情况下,你应该使用iterateM_或类似的东西,而不是mfix.另见iterate + forever = iterateM?通过反馈重复操作.
不幸的是,mfix在IOmonad 中并不能真正产生这样的零碎列表。这是因为 monad 中的大多数操作都非常严格:在执行整个操作之前,IO它们不会产生结果的任何部分。特别是,在遍历完所有输入列表之前,mapMin不会返回其结果列表的任何部分,这使得这里没有希望以正确的方式喜结连理。IOmfix
一般来说,只有在整个操作完成之前不严格查看绑定值的情况下,mfixin才有效。这仍然有一些可能的用途,例如仅使用可变单元周期来初始化数据结构。IOmfixnewIORef