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