Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trick GHC into an infinite loop using strictness annotations

Tags:

haskell

For some reason I need to trick Haskell into executing an infinite loop rather than just detecting it and exiting with <<loop>>, but it's too smart to do it. Are there any handy examples where a loop is caused by a strictness annotation (!), or can I turn off the loop detection? Currently I'm doing

x = x + y
y = x + y
inf !n = 0

main = do
    print $ inf x
like image 389
rem Avatar asked Dec 05 '22 02:12

rem


2 Answers

This should do it:

{-# LANGUAGE BangPatterns #-}
f :: Int -> Int
f x = if x >= 0 then f (x + 1) else 0

fun !n = 0

main = print $ fun $ f 0

GHC does indeed seem to be pretty clever at spotting the loop, before I added the if it even spotted the recursive function call.

I've tested this with -O3 on GHC 7.10.

EDIT: added the extra function fun as suggested by the asker, just to satisfy the requirement that a bang pattern be involved. It's not really relevant to the loop itself. Also changed from Integer to Int as suggested by another answer, to ensure constant memory usage.

like image 64
GS - Apologise to Monica Avatar answered Feb 23 '23 10:02

GS - Apologise to Monica


The "problem" is that while ghc is evaluating a thunk, it replaces it with a blackhole. That's why something like the following simple example would not work:

inf :: ()
inf = inf

main :: IO ()
main = return $! inf

Ghc will force inf, that will cause it to force inf a second time. It will realize inf has been blackholed and detect a loop.

The solution proposed of an infinite counter will work, but will allocate more and more memory for the integer counter. It probably doesn't matter, since the memory for an Integer is logarithmic, but it might be slightly more elegant to use an Int and just let the counter wrap.

Another option would be to use an infinite data structure. That will create garbage, but in steady state consume a fixed amount of allocated memory. Something like:

main :: IO ()
main = return $! last $ repeat ()
like image 42
user3188445 Avatar answered Feb 23 '23 09:02

user3188445