haskell - 如何强制评估 `unsafePerformIO` 内的 IO 操作?

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/

相关文章:

r - 在 R 中生成所有可能的长度为 n 的二进制向量

sql - 在PostgreSQL中获取2个字符之间的子字符串

javascript - 如何将 jointjs 与 reactjs 一起使用?

google-cloud-platform - 在 Google Cloud Storage 上恢复

ios - 更改模型后的核心数据迁移

r - 在 R 中使用什么包进行 Kmeans 预测?

wordpress - WooCommerce REST API : query products

python - 需要字段的 django rest 框架错误

python - 基于字符串值列表的条形图

ruby - 如何将 --help, -h 标志添加到 Thor 命令?