I have this Haskell code which when compiled with GHC and run, aborts with a loop detected.
data Foo = Foo ()
deriving (Eq,Show)
type Foop = Foo -> ((),Foo)
noOp :: Foop
noOp st = ((),st)
someOp :: Foop
someOp st@(Foo x) = ((),st)
(<+>) :: Foop -> Foop -> Foop
(<+>) f g st = let ((_,st'),(_,st'')) = ((f st),(g st')) in ((),st'')
main = print $ (noOp <+> someOp) $ Foo ()
I think it shouldn't, and here are some modifications. Each of them makes the loop go away:
data Foo to newtype Foo
(noOp <+> someOp) to (someOp <+> noOp)
@(Foo x)
Is this a bug in GHC or is it my lack of understanding the evaluation process?
(_, _) demands WHNF of (f st, g st'). Easy.(_, (_,_)) demands WHNF of g st'. Here's the problem, because g is strict, hence it first needs st' in WHNF too. The runtime finds st' in the pattern ((_,st'), (_,_)), so before it can actually traverse down into st' it needs to WHNF both of the tuples. Because g is strict however, this first requires st'... and so on.If you match the result of g with a lazy irrefutable pattern
(<+>) f g st = let ((_,st'), ~(_,st'')) = (f st, g st') in ((),st'')
then the problem goes away, because this is evaluated thus:
(_, _) demands WHNF of (f st, g st'). Easy.(_, ~(_,_)) demands nothing more for the time being.((_,st'), ~(_,_)) demands WHNF of f st. Fortunately, we can fulfill this, because st does not depend on the pattern.in ((),st''). Only at this point is the irrefutable pattern ~(_,st'') forced, but this is now not a problem anymore because st' is available here, so it's just a matter of computing g once.The fixes you've tried all amount to making g non-strict:
remove the deconstruction
@(Foo _)
Without that, g doesn't really need to look at its argument to construct the result skeleton, i.e. the tuple match (_,st'') can then succeed without first requiring WHNF of st'.
change
data Footonewtype Foo
This has the effect that the Foo constructor doesn't actually exist at runtime, so there's nothing that the pattern st@(Foo _) would force.
change
noOp <+> someOptosomeOp <+> noOp
As I said, the loop only comes about because g is strict. If you put f in its position, which is not strict, then there's no problem.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With