简体   繁体   中英

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. 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).

Unfortunately, your main function:

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

desugars (as per the Haskell98 report) into:

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 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.

Since pattern matching the "pure" value to its constructor (namely () ) would force the value and so execute the unsafe action, if you write:

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:

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.

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 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:

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.

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

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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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