David Feuer

2018-07-08 16:40:13 UTC

I filed a ticket[*] for this, but I think maybe the libraries list should

weigh in on whether it is something that should be fixed. In general, fixST

f is supposed to bottom out if f forces its argument. However, the lazy way

GHC blackholes thunks under evaluation sometimes leads to the computation

being run again. In certain contrived situations, this can allow the

computation to succeed!

The example I give in the ticket:

import Control.Monad.ST.Strict

import Control.Monad.Fix

import Data.STRef

foo :: ST s Int

foo = do

ref <- newSTRef True

mfix $ \res -> do

x <- readSTRef ref

if x

then do

writeSTRef ref False

return $! res + 5 -- force the final result

else return 10

main = print $ runST foo

Here, the computation writes to an STRef before forcing the final result.

Forcing the final result causes the computation to run again, this time

taking the other branch. The program prints 15. When compiled with -O

-feager-blackholing, however, the expected <<loop>> exception occurs.

As far as I know, this weirdness never changes the value produced by a

non-bottoming computation, and never changes a non-bottoming computation

into a bottoming one. The fix (defining fixST the way fixIO is currently

defined) would have a slight performance impact. Is it worth it?

[*] https://ghc.haskell.org/trac/ghc/ticket/15349

weigh in on whether it is something that should be fixed. In general, fixST

f is supposed to bottom out if f forces its argument. However, the lazy way

GHC blackholes thunks under evaluation sometimes leads to the computation

being run again. In certain contrived situations, this can allow the

computation to succeed!

The example I give in the ticket:

import Control.Monad.ST.Strict

import Control.Monad.Fix

import Data.STRef

foo :: ST s Int

foo = do

ref <- newSTRef True

mfix $ \res -> do

x <- readSTRef ref

if x

then do

writeSTRef ref False

return $! res + 5 -- force the final result

else return 10

main = print $ runST foo

Here, the computation writes to an STRef before forcing the final result.

Forcing the final result causes the computation to run again, this time

taking the other branch. The program prints 15. When compiled with -O

-feager-blackholing, however, the expected <<loop>> exception occurs.

As far as I know, this weirdness never changes the value produced by a

non-bottoming computation, and never changes a non-bottoming computation

into a bottoming one. The fix (defining fixST the way fixIO is currently

defined) would have a slight performance impact. Is it worth it?

[*] https://ghc.haskell.org/trac/ghc/ticket/15349