11

Given this code snippet:

someFunction x = print x `seq` 1

main = do print (someFunction "test")

why doesn't the print x print test when the code is executed?

$./seq_test 
1

If I replace it with error I can check that the left operand of seq is indeed evaluated.

How could I achieve my expected output:

test
1

modifying only someFunction?

Bakuriu
  • 98,325
  • 22
  • 197
  • 231
  • 2
    Evaluating an IO action is not the same as executing it. There isn't really any way to make someFunction do what you want, because if you want to use `print` in it, it has to be in the IO monad and you won't be able to print its result (without modifying main). – fjh Dec 02 '13 at 09:31
  • 2
    I wonder why the downvote? I belive my question is a perfectly valid question. – Bakuriu Jul 15 '14 at 20:48
  • Related: https://stackoverflow.com/questions/24775528/why-cant-haskell-be-tricked-into-performing-io-operations-by-using-strict-evalu . – atravers Oct 06 '20 at 09:30

2 Answers2

13

Evaluating an IO action does nothing whatsoever. That's right!

If you like, values of IO type are merely "instruction lists". So all you do with that seq is force the program to be sure1 of what should be done if the action was actually used. And using an action has nothing to do with evaluation, it means monadically binding it to the main call. But since, as you say, someFunction is a function with a non-monadic signature, that can't happen here.

What you can do... but don't, is

import Foreign

someFunction x = unsafePerformIO (print x) `seq` 1

this actually couples evaluation to IO execution. Which normally is a really bad idea in Haskell, since evaluation can happen at completely unforseeable order, possibly a different number of times than you think (because the compiler assumes referential transparency), and other mayhem scenarios.

The correct solution is to change the signature to be monadic:

someFunction :: Int -> IO Int
someFunction x = do
     print x
     return 1

main = do
     y <- someFunction "test"
     print y

1And as it happens, the program is as sure as possible anyway, even without seq. Any more details can only be obtained by executing the action.

leftaroundabout
  • 117,950
  • 5
  • 174
  • 319
  • So you are saying that if a library requires a function with a certain non-monadic signature, there is absolutely no way in Haskell to safely add any output during its execution and continue using the library? – Bakuriu Dec 02 '13 at 12:17
  • 3
    Yup, that's right and intentional. Like it or hate it. (Again, there is `Debug.Trace`, for debugging – but for anything else but debugging this is indeed just as unsafe as any other way to achieve output inside a pure function.) — Seriously, if you write idiomatic Haskell you'll feel much less _need_ for such ad-hoc logging than in other languages. – leftaroundabout Dec 02 '13 at 12:28
  • I'd agree if libraries had a good documentation about their monadic features. E.g. Alex and Happy do *not* provide a good documentation, and I even heard by some users that some of the documentation is actually wrong (which is worse than no-documentation). If I have a *requirement* about this kind of output I'll then have to resume the do-random-change-and-guess-the-implementation approach in order to use them. If you happen to know how to use them, please answer my other question. – Bakuriu Dec 02 '13 at 12:34
  • I don't know much anything about Alex and Happy, but... what you're doing sounds indeed horrible. Why don't you just look up the _source_ if you try to find something out about the implementation? – leftaroundabout Dec 02 '13 at 12:47
  • Reading the source code cannot be a replacement of the documentation. I'm not a Haskell expert and I don't have days or weeks to spend trying to understand some thousand of lines of code. – Bakuriu Dec 02 '13 at 13:14
  • I don't know what kind of work you're doing there. Often in Haskell, it's quite easy do understand what an undocumented function does already from the type signature alone, or from toying around with it in GHCi. Inserting trace statements into a full programm OTOH is certainly not more efficient than reading the source code. – leftaroundabout Dec 02 '13 at 13:22
  • 2
    If you are adding the print statements to get a better understanding of how some complicated code is executing, then that's like debugging. You can use Debug.Trace to get output at random places, and when you understand the code, you remove those calls again. – augustss Dec 02 '13 at 13:24
  • @augustss You got it the wrong way. The output *during the parsing* phase is a *requirement*. I *do* have tried to *add* other output to debug a bit *and* tried playing around with the functions in GHCi etc. However, even after this I cannot understand how to use that interface. – Bakuriu Dec 02 '13 at 17:32
  • 2
    If output during parsing is a requirement (it sounds like a strange requirement; you should be able to interleave parsing and printing) then your function has to have IO type. – augustss Dec 03 '13 at 00:39
  • using $> from Data.Functor can make the do block pattern of someFunction more terse – Jeff Huang Aug 10 '22 at 23:50
1

seq evaluated expressions to weak head normal form, which is simply the outermost constructor (or lambda application). The expression print x is already in WHNF, so seq doesn't do anything.

You can get the result you're looking for with the function Debug.Trace.trace.

John L
  • 27,937
  • 4
  • 73
  • 88
  • It doesn't matter if it's in WHNF for the IO purpose. — `trace` is a good suggestion – though, as the module name tells, really just for debugging purposes. – leftaroundabout Dec 02 '13 at 09:52
  • @leftaroundabout I know it doesn't matter for the result. But these are tricky issues, and it's better to be precise. – kosmikus Dec 02 '13 at 10:02
  • @kosmikus I think an IO action is a function value wrapped in a newtype constructor - i.e., clearly WHNF – Ingo Dec 02 '13 at 11:05
  • @Ingo `print` is a function, and `print x` is a function call. There's no constructor anywhere ;) – kosmikus Dec 02 '13 at 11:13
  • @kosmikus - Sure, `print x` has type `IO ()` and this is something like `IO (\RealWorld -> ...)` – Ingo Dec 02 '13 at 11:16
  • @Ingo: no, he's right: `print x` ab initio is a thunk referring to application of the `print` function to the value `x`. – leftaroundabout Dec 02 '13 at 11:21
  • @leftaroundabout - Yes, that's true, and seq evaluates that thunk to WHNF, which is whatever the representation of a value of type `IO ()` is. You surely don't tell me that `IO ()` does not have some representation, do you? – Ingo Dec 02 '13 at 11:24
  • 5
    @Ingo My remark was about "The expression `print x` is already in WHNF, so `seq` doesn't do anything." Which is wrong, because `print x` is not in WHNF, even though it can certainly evaluated to WHNF, and will be by `seq`. That's all. Can we end this discussion now? – kosmikus Dec 02 '13 at 11:27