Skip to content

DeriveFoldable rejects instances with constraints in last argument of data type

Currently, the -XDeriveFoldable extension will reject any derived Foldable instance for a data type where the last argument of the type constructor is constrained. For example, using this data type from TcDeriv.hs as inspiration:

{-# LANGUAGE DeriveFoldable, GADTs, StandaloneDeriving #-}
module DeriveFoldableRejected where

data T a b where
    T4 :: Ord b => b -> T a b
    T5 :: b -> T b b
    T6 :: T a (b,b)

deriving instance Foldable (T a)

Compiling DeriveFoldableRejected.hs with GHC 7.10 will currently fail:

DeriveFoldableRejected.hs:9:1:
    Can't make a derived instance of ‘Foldable (T a)’:
      Constructor ‘T4’ must be truly polymorphic in the last argument of the data type
    In the stand-alone deriving instance for ‘Foldable (T a)’
Failed, modules loaded: none.

I don't think this restriction needs to apply to Foldable instances. Unlike Functor and Traversable instances, which require the last argument to be truly universal, Foldable instances can get away without this. To demonstrate, here's a slightly modified T data type, without the constraints:

{-# LANGUAGE DeriveFoldable, GADTs, StandaloneDeriving #-}
{-# OPTIONS_GHC -ddump-deriv #-}
module DeriveFoldableLegal where

data T a b where
    T45 :: b -> T a b
    T6  :: T a b

deriving instance Foldable (T a)

The output of -ddump-deriv is:

Derived instances:
  instance Data.Foldable.Foldable (DeriveFoldableLegal.T a_aDc) where
    Data.Foldable.foldr f_aDd z_aDe (DeriveFoldableLegal.T45 a1_aDf)
      = f_aDd a1_aDf z_aDe
    Data.Foldable.foldr f_aDg z_aDh DeriveFoldableLegal.T6 = z_aDh
    Data.Foldable.foldMap f_aDi (DeriveFoldableLegal.T45 a1_aDj)
      = f_aDi a1_aDj
    Data.Foldable.foldMap f_aDk DeriveFoldableLegal.T6
      = GHC.Base.mempty

Copying this back into DeriveFoldableRejected.hs (after some cleanup):

{-# LANGUAGE DeriveFoldable, GADTs, StandaloneDeriving #-}
module DeriveFoldableRejected where

data T a b where
    T4 :: Ord b => b -> T a b
    T5 :: b -> T b b
    T6 :: T a (b,b)

instance Foldable (T a) where
    foldr f z (T4 a) = f a z
    foldr f z (T5 a) = f a z
    foldr f z T6     = z
    
    foldMap f (T4 a) = f a
    foldMap f (T5 a) = f a
    foldMap f T6     = mempty

reveals that it will compile correctly with the generated code. Therefore, it seems like the check for universality in the last type argument shouldn't be used in -XDeriveFoldable.

Edited by Ryan Scott
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information