import System.IO.Unsafe
main :: IO ()
main = do
_ <- return $ unsafePerformIO $ do
print "test2"
print "test"
以上仅输出 test
(当通过 runghc
运行时),我假设由于 Haskell 的惰性性质,第一个 IO 操作实际上没有得到评估。我怎样才能强制它评估和执行操作?
以上仅用于调试目的。
最佳答案
为了执行 IO 副作用,需要强制使用 unsafePerformIO
创建的“纯”值(即,至少评估为 WHNF)。
不幸的是,您的 main
函数:
main = do
_ <- return $ unsafePerformIO $ do
print "test2"
print "test"
脱糖(根据 Haskell98 报告)为:
main = let ok _ = do { print "test" }
ok _ = fail "..."
in return (unsafePerformIO (print "test2")) >>= ok
根据 monad 法则等价于:
main = let ok _ = do { print "test" }
ok _ = fail "..."
in ok (unsafePerformIO (print "test2"))
遗憾的是,let
绑定(bind)的第一部分:
let ok _ = do { print "test2" }
在调用时不使用——因此不强制——它的参数,因此忽略“纯”不安全 IO 操作。
由于将“纯”值与其构造函数(即 ()
)进行模式匹配,会强制该值并因此执行不安全的操作,如果您这样写:
main = do
() <- return $ unsafePerformIO $ do
print "test2"
print "test"
这样就可以了。
还有其他方法可以强制执行此操作。您可以明确地进行模式匹配:
main = do
case unsafePerformIO (print "test2") of
() -> return ()
print "test"
或者像这样使用seq
:
main = do
unsafePerformIO (print "test2") `seq` print "test"
或者像这样:
main = do
unsafePerformIO (print "test2") `seq` return ()
print "test"
或者使用严格的求值运算符:
main = do
return $! unsafePerformIO (print "test2")
print "test"
您还可以按照@Chris Stryczynski 的建议使用BangPatterns
扩展。
我不确定哪个最好,但我倾向于使用 ($!)
运算符作为最惯用的运算符。
如@Carl 所述,出于调试目的,使用 Debug.Trace
中的 trace
通常比直接调用 unsafePerformIO
更明智,两者因为它是从纯代码打印调试信息的标准方式,而且因为实现比简单的 unsafePerformIO 更周到一点。 putStrLn
。但是,它可能会出现相同的问题:
import Debug.Trace
main = do
return $ trace "test2" () -- won't actually print anything
print "test"
相反,您需要强制它的值,也许使用上述方法之一:
import Debug.Trace
main = do
return $! trace "test2" () -- with ($!) this works
print "test"
当@Carl 说 trace
有一个 API 可以解决这个问题时,他的意思是您通常在以下情况下使用 trace:
let value_i_actually_use = trace "my message" value_of_the_trace_expression
当您实际使用(评估)trace
表达式的值时,将显示消息。
然后
关于haskell - 如何强制评估 `unsafePerformIO` 内的 IO 操作?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48977455/