Ticket #3819 (closed proposal: fixed)
keeping RelaxedPolyRec as optional feature can help spotting infinite recursion
This is a ticket you have nothing to do about - isn't this great? I just want to give a real-world case where the current behaviour helped me to detect an infinite recursion early.
I wrote the following code, that implements a poke for any Traversable container with Storable elements:
poke :: (Fold.Foldable f, Storable a) => Ptr (f a) -> f a -> IO () poke ptr x = evalStateT (Fold.traverse_ pokeState x) $ castPtr ptr pokeState :: (Storable a) => a -> StateT (Ptr a) IO () pokeState x = do liftIO . flip poke x =<< get modify (flip advancePtr 1)
You can find this code here:
When I compiled this I got the compiler error:
src/Foreign/Storable/Traversable.hs:67:0: Contexts differ in length (Use -XRelaxedPolyRec to allow this) When matching the contexts of the signatures for poke :: forall (f :: * -> *) a. (Fold.Foldable f, Storable a) => Ptr (f a) -> f a -> IO () pokeState :: forall a. (Storable a) => a -> StateT (Ptr a) IO () The signature contexts in a mutually recursive group should all be identical When generalising the type(s) for poke, pokeState
This quickly pointed me to the problem, that the call to poke in pokeState actually was wrong. It must be Storable.poke. If GHC had compiled this, it would have certainly gone into an infinite recursion.
There are two ways to treat this example: * Blame my naming style where I re-use common identifiers and distinguish them later by qualification. * Count it as vote for keeping the RelaxedPolyRec? as optional feature.