简体   繁体   English

如何在`unsafePerformIO`中强制评估IO操作?

[英]How do I force evaluation of an IO action within `unsafePerformIO`?

import System.IO.Unsafe

main :: IO ()
main = do
  _ <- return $ unsafePerformIO $ do
    print "test2"
  print "test"

The above only outputs test (when run via runghc ), I'm assuming due the lazy nature of Haskell that the first IO action does not actually get evaluated. 上面仅输出test (通过runghc运行时),由于Haskell的惰性,我假设第一个IO操作实际上没有得到评估。 How can I force it to evaluate and perform the action? 如何强制其评估和执行操作?

The above is for debugging purposes only. 上面仅用于调试目的。

In order for the IO side-effect to be executed, the "pure" value created by unsafePerformIO needs to be forced (ie, evaluated at least to WHNF). 为了执行IO副作用,需要强制使用unsafePerformIO创建的“ pure”值(即至少评估为WHNF)。

Unfortunately, your main function: 不幸的是,您的main功能是:

main = do
  _ <- return $ unsafePerformIO $ do
    print "test2"
  print "test"

desugars (as per the Haskell98 report) into: 将desugars(根据Haskell98报告)分为:

main = let ok _ = do { print "test" }
           ok _ = fail "..."
       in return (unsafePerformIO (print "test2")) >>= ok

which is equivalent by the monad laws to: 单子法则等效于:

main = let ok _ = do { print "test" }
           ok _ = fail "..."
       in ok (unsafePerformIO (print "test2"))

Sadly, the first part of the let binding: 可悲的是, let绑定的第一部分:

let ok _ = do { print "test2" }

doesn't use -- and so doesn't force -- its argument when it's called, so the "pure" unsafe IO action is ignored. 不会在调用时使用它的参数,因此也不会强制使用它,因此将忽略“纯”不安全的IO操作。

Since pattern matching the "pure" value to its constructor (namely () ) would force the value and so execute the unsafe action, if you write: 由于将“ pure”值与其构造函数(即() )相匹配的模式强制使用该值,因此如果执行以下操作, 执行不安全的操作:

main = do
  () <- return $ unsafePerformIO $ do
    print "test2"
  print "test"

then that will work fine. 那就可以了

There are other ways to force the action. 还有其他方法可以强制执行此操作。 You could pattern match explicitly: 您可以显式地进行模式匹配:

main = do
  case unsafePerformIO (print "test2") of
    () -> return ()
  print "test"

or use seq either like so: 或使用seq就像这样:

main = do
  unsafePerformIO (print "test2") `seq` print "test"

or like so: 或像这样:

main = do
  unsafePerformIO (print "test2") `seq` return ()
  print "test"

or use the strict evaluation operator: 或使用严格的评估运算符:

main = do
  return $! unsafePerformIO (print "test2")
  print "test"

You can also use the BangPatterns extension as suggested by @Chris Stryczynski. 您也可以使用BangPatterns建议的BangPatterns扩展名。

I'm not sure which is best, though I'd lean towards using the ($!) operator as the most idiomatic. 我不确定哪个最好,尽管我倾向于使用($!)运算符作为最惯用的方法。

As noted by @Carl, for debugging purposes, using trace from Debug.Trace is generally more sensible than calling unsafePerformIO directly, both because it's the standard way of printing debugging information from pure code and because the implementation is a little more thoughtful than a simple unsafePerformIO . putStrLn 正如@Carl所指出的,出于调试目的,使用Debug.Trace trace通常比直接调用unsafePerformIO更明智,这是因为这是从纯代码中打印调试信息的标准方式,而且实现比简单简单得多unsafePerformIO . putStrLn unsafePerformIO . putStrLn . unsafePerformIO . putStrLn However, it potentially exhibits the same issue: 但是,它可能会出现相同的问题:

import Debug.Trace
main = do
  return $ trace "test2" ()  -- won't actually print anything
  print "test"

Instead, you need to force it's value, perhaps by using one of the methods above: 相反,您可能需要使用上面的一种方法来强制其值:

import Debug.Trace
main = do
  return $! trace "test2" ()  -- with ($!) this works
  print "test"

When @Carl says that trace has an API that works around this issue, he means that you normally use trace in a situation like: 当@Carl说trace具有解决此问题的API时,他表示您通常在以下情况下使用跟踪:

let value_i_actually_use = trace "my message" value_of_the_trace_expression

When you actually use (evaluate) the value of the trace expression, then the message will be displayed. 当您实际使用 (评估) trace表达式的值时,将显示该消息。

and then 接着

Potentially there are some limitations with the below (mentioned in the comments of the referenced answer). 以下内容可能有一些限制(在引用答案的注释中提到)。

More information here: https://stackoverflow.com/a/14163739/1663462 此处了解更多信息: https : //stackoverflow.com/a/14163739/1663462

{-# LANGUAGE BangPatterns #-}
import System.IO.Unsafe

main :: IO ()
main = do
  let !_ = unsafePerformIO $ do
         print "test2"
  print "test"

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM