Simon Peyton-Jones wrote:
| I've got an inner loop that I think I can see is strict in the Int
| argument being passed around, but that GHC 6.6.1 isn't unboxing. In the
| following example both functions take a GHC.Base.Int, which I think
| should be an Int#.

OK this is an interesting one. Here's the smallest program that demonstrates 
the problem.

foreign import ccall unsafe "stdio.h getchar" getchar :: IO CInt

f56 :: State# RealWorld -> Int -> Int
f56 s v2 = case (unIO getchar s) of
           (# s' , v6  #) ->
              case v2 of I# _ -> f56 s' v2

GHC says this is lazy in v2, which it obviously isn't.  Why?  Because there's a 
special hack (introduced after an earlier bug report) in the strictness 
analyser to account for the fact that a ccall might exit the program.  Suppose 
instead of calling 'getchar' we called 'exit'!  Then f56 is not strict in v2 
any more.

Here was a larger program that demonstrated the problem:

        do { let len = <expensive> ;
           ; when (...) (exitWith ExitSuccess)
           ; print len }

Suppose exitWith doesn't exit; it loops or returns. Then 'len' is sure to be 
evaluated, and GHC will evaluate it before the 'when'.

exitWith in fact doesn't exit: it raises the exit exception, which is caught by the top-level exception handler, which finally arranges to exit. So I imagine the strictness analyser inferred that exitWith returns bottom, and hence it was justified in evaluating len first.

This doesn't seem specific to exit, to me. Throwing any exception would trigger this behaviour. Indeed, since we're in the IO monad, I might reasonably expect to have greater control over the evaluation order, and perhaps GHC is right - the strictness analyser should not cause something to be evaluated earlier than normal if that means moving it past a possible effect. In fact this behaviour seems to be essential if we are to be able to use lazy I/O in a sensible way, because otherwise lazy I/O can be evaluated earlier than we expect:

  do
    s <- getContents
    putStr "prompt:"; hFlush stdout
    case s of ...

We are sure to evaluate s, but we better not do it before the putStr (I'm sure the strictness analyser won't do this right now, because it won't infer that putStr returns, but imagine some simpler IO instead).

I'm not quite sure what to make of this. On the one hand it's ugly, because we're forced into an evaluation order. But even if it weren't for lazy I/O, I am tempted to think that the IO monad ought to restrict evaluation order, if only so that we can have more control when we want it. So perhaps GHC is doing the right thing.

Cheers,
        Simon

_______________________________________________
Glasgow-haskell-users mailing list
Glasgow-haskell-users@haskell.org
http://www.haskell.org/mailman/listinfo/glasgow-haskell-users

Reply via email to