0

When I try this:

import GHC.Generics (Generic)
import Control.DeepSeq (NFData(..))
import Data.Vector.Generic (Vector)

data Entry a = Entry !Bool a
             deriving (Generic, NFData)

-- The variable @v@ is meant to be instantiated with a 'Vector'
-- type.  Most operations for the type have a @Vector v (Entry a)@
-- constraint.
newtype DenseIntMap v a = DenseIntMap (v (Entry a))

instance NFData (v (Entry a)) => NFData (DenseIntMap v a) where
  rnf (DenseIntMap va) = rnf va

...I get this error:

/Users/casillas/GitHub/tau-sigma/src/TauSigma/Util/DenseIntMap.hs:53:10:
    Constraint is no smaller than the instance head
      in the constraint: Vector v (Entry a)
    (Use UndecidableInstances to permit this)
    In the instance declaration for ‘NFData (DenseIntMap v a)’

/Users/casillas/GitHub/tau-sigma/src/TauSigma/Util/DenseIntMap.hs:53:10:
    Constraint is no smaller than the instance head
      in the constraint: NFData (v (Entry a))
    (Use UndecidableInstances to permit this)
    In the instance declaration for ‘NFData (DenseIntMap v a)’

Using UndecidableInstances indeed makes it go away, but I'm wary of using that extension. Is there some other way to make things work in this case? (Without changing the types too much, preferably.)

Luis Casillas
  • 29,802
  • 7
  • 49
  • 102
  • Why do you want that `Vector` constraint? It doesn't seem to have any purpose. – dfeuer Jan 05 '16 at 06:26
  • @dfeuer: Oh, yeah, you're right. When I remove it, however, I still do get the `NFData v (Entry a)` error if I don't use the extension. I'll edit the question. – Luis Casillas Jan 05 '16 at 06:32
  • Also, why are you wary of `UndecidableInstances`? Haskell has a really pitiful excuse for a termination checker. – dfeuer Jan 05 '16 at 06:33
  • @dfeuer: Not out of any actual knowledge, but rather because "undecidable" is a scary word. – Luis Casillas Jan 05 '16 at 06:38

1 Answers1

5

Warning: I haven't tested any of this code.

The approach that seems cleanest to me is to follow a Prelude.Extras-style path:

class NFData1 f where
  rnf1 :: NFData a => f a -> ()

You can now write, for each vector type, something like

instance NFData1 V where
  rnf1 = rnf

And then

instance (NFData1 v, NFData a) => NFData (DenseIntMap v a) where ...

An alternative approach that may fit your current code better is to think about v as a Vector explicitly. Instead of worrying about how v a would prefer to force itself, ram your own notion down its throat by folding: something like

instance (Vector v a, NFData a) => NFData (DenseIntMap v a) where
  rnf = V.foldl' (\() e -> rnf e) ()

This second approach seems likely to play poorly with vector fusion unless you're careful about which vectors you want to force from left to right and which from right to left.

dfeuer
  • 48,079
  • 5
  • 63
  • 167