Use `seq` and print in haskell
Asked Answered
I

1

1

I know that this is a little bit tricky but i wonder why it doesn't work!

module Main where

sillyDebug :: Int -> Int -> Int
sillyDebug x y =
    (print x) `seq` (x + y)

main :: IO ()
main = do
    print (sillyDebug 1 2)

while its ideal is the same as

sillyDebug = (trace (show x) False) `seq` (x + y)

Is it related to lazy evaluation or side effect in haskell? https://hackhands.com/lazy-evaluation-works-haskell/

Iq answered 2/2, 2017 at 4:57 Comment(4)
Please see stackoverflow.com/help/mcve. Most of your imports are irrelevant, here. sillyIO is defined nowhere. sillyDebug = (trace (show x) False) `seq` (x + y) doesn't even compile. And describe the problem you're facing. What did you expect? What do you get?Fere
Sorry, it's my fault :3Iq
This problem is about the difference between evaluating an expression and executing a monadic action, but I think it’s also worth making a note about seq. a `seq` b doesn’t exactly force evaluation of a. It generates a dependency between thunks, so if b is evaluated, then a is too. print (snd (x `seq` y, z)) never evaluates x because it never evaluates y.Tennes
Related: #24776028 .Duhon
R
4

Merely evaluating some IO action doesn’t do anything at all. You can think of IO sort of like a really big sum type of all the possible side-effectful things Haskell can do, even if it isn’t actually implemented like that at all. Something like this:

data IO a where
  PutStrLn :: String -> IO ()
  ReadFile :: FilePath -> IO String
  ExitWith :: ExitCode -> IO a
  ...

One of the IO constructors in this theoretical visualization would be a Sequence constructor, with a type signature like this:

  Sequence :: IO a -> (a -> IO b) -> IO b

This constructor is used to implement >>= for the IO type.

Inside of GHC is a magical function called magicallyExecuteIO with type IO a -> a, which coordinates for each action to actually perform its corresponding side-effect. (Incidentally, this function is also sometimes pronounced unsafePerformIO.) GHC implicitly calls magicallyExecuteIO on the result of your program’s main function, as well as on expressions written in GHCi.

However, without using magicallyExecuteIO, evaluating one of the IO constructors like PutStrLn doesn’t do anything. In this implementation, it would just work like any other data constructor:

ghci> Just (PutStrLn "hello!")
Just (PutStrLn "hello!") :: Maybe (IO ())

(I’ve wrapped it with Just to prevent GHCi from running the IO action.)

Of course, GHC’s actual IO type isn’t implemented this way, but that’s really just an implementation detail. Evaluating an IO a value doesn’t cause a side-effect to happen any more than evaluating a Maybe a value does. Only magicallyExecuteIO can do that.

Rone answered 2/2, 2017 at 5:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.