[GHC] #10447: DeriveFoldable rejects instances with constraints in last argument of data type

GHC ghc-devs at haskell.org
Wed May 27 00:25:28 UTC 2015


#10447: DeriveFoldable rejects instances with constraints in last argument of data
type
-------------------------------------+-------------------------------------
        Reporter:  RyanGlScott       |                   Owner:
            Type:  bug               |                  Status:  new
        Priority:  normal            |               Milestone:
       Component:  Compiler          |                 Version:  7.10.1
      Resolution:                    |                Keywords:
Operating System:  Unknown/Multiple  |            Architecture:
 Type of failure:  GHC rejects       |  Unknown/Multiple
  valid program                      |               Test Case:
      Blocked By:                    |                Blocking:
 Related Tickets:  #8678             |  Differential Revisions:
-------------------------------------+-------------------------------------

Comment (by RyanGlScott):

 To be a bit more explicit, {{{TcDeriv}}} specifies
 [http://git.haskell.org/ghc.git/blob/9f968e97a0de9c2509da00f6337b612dd72a0389:/compiler/typecheck/TcDeriv.hs#l1331
 these rules] for {{{DeriveFunctor}}}, {{{DeriveFoldable}}}, and
 {{{DeriveTraversable}}}:

 {{{
 -- OK for Functor/Foldable/Traversable class
 -- Currently: (a) at least one argument
 --            (b) don't use argument contravariantly
 --            (c) don't use argument in the wrong place, e.g. data T a = T
 (X a a)
 --            (d) optionally: don't use function types
 --            (e) no "stupid context" on data type
 }}}

 These conditions are OK. I propose that
 [http://git.haskell.org/ghc.git/blob/9f968e97a0de9c2509da00f6337b612dd72a0389:/compiler/typecheck/TcDeriv.hs#l1425
 these additional rules] from {{{TcDeriv}}} only be used for
 {{{DeriveFunctor}}} and {{{DeriveTraversable}}}, not for
 {{{DeriveFoldable}}}:

 {{{
 Note [Check that the type variable is truly universal]
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 For Functor, Foldable, Traversable, we must check that the *last argument*
 of the type constructor is used truly universally quantified.  Example

    data T a b where
      T1 :: a -> b -> T a b      -- Fine! Vanilla H-98
      T2 :: b -> c -> T a b      -- Fine! Existential c, but we can still
 map over 'b'
      T3 :: b -> T Int b         -- Fine! Constraint 'a', but 'b' is still
 polymorphic
      T4 :: Ord b => b -> T a b  -- No!  'b' is constrained
      T5 :: b -> T b b           -- No!  'b' is constrained
      T6 :: T a (b,b)            -- No!  'b' is constrained

 Notice that only the first of these constructors is vanilla H-98. We only
 need to take care about the last argument (b in this case).  See Trac
 #8678.
 Eg. for T1-T3 we can write

      fmap f (T1 a b) = T1 a (f b)
      fmap f (T2 b c) = T2 (f b) c
      fmap f (T3 x)   = T3 (f x)
 }}}

 These rules must hold for {{{Functor}}} and {{{Traversable}}} instances
 because the type signatures of {{{fmap :: Functor t => (a -> b) -> t a ->
 t b}}} and {{{traverse :: (Applicative f, Traversable t) => (a -> f b) ->
 t a -> f (t b)}}} require that the last type argument be truly
 polymorphic, since it must be instantiated with two arbitrary types, {{{t
 a}}} and {{{t b}}}. As evidence, if you try to make a {{{Functor T}}}
 instance using what {{{DeriveFunctor}}} would (hypothetically) generate:

 {{{#!hs
 {-# LANGUAGE GADTs #-}
 module T where

 data T a where
     MkT :: Int -> T Int

 instance Functor T where
     fmap f (MkT a) = MkT (f a)
 }}}

 it will be rejected:

 {{{
 T.hs:8:22:
     Could not deduce (b ~ Int)
     from the context (a ~ Int)
       bound by a pattern with constructor
                  MkT :: Int -> T Int,
                in an equation for ‘fmap’
       at T.hs:8:13-17
       ‘b’ is a rigid type variable bound by
           the type signature for fmap :: (a -> b) -> T a -> T b at
 T.hs:8:5
     Expected type: T b
       Actual type: T Int
     Relevant bindings include
       f :: a -> b (bound at T.hs:8:10)
       fmap :: (a -> b) -> T a -> T b (bound at T.hs:8:5)
     In the expression: MkT (f a)
     In an equation for ‘fmap’: fmap f (MkT a) = MkT (f a)
 }}}

 On the other hand, {{{Foldable}}} instances do not require the last type
 argument to be truly polymorphic, since all of the {{{Foldable}}} methods
 only parameterize {{{t}}} over a single type variable:

 {{{
 class Foldable t where
     fold :: Monoid m => t m -> m
     foldMap :: Monoid m => (a -> m) -> t a -> m
     foldr :: (a -> b -> b) -> b -> t a -> b
     foldr' :: (a -> b -> b) -> b -> t a -> b
     foldl :: (b -> a -> b) -> b -> t a -> b
     foldl' :: (b -> a -> b) -> b -> t a -> b
     foldr1 :: (a -> a -> a) -> t a -> a
     foldl1 :: (a -> a -> a) -> t a -> a
     toList :: t a -> [a]
     null :: t a -> Bool
     length :: t a -> Int
     elem :: Eq a => a -> t a -> Bool
     maximum :: forall a. Ord a => t a -> a
     minimum :: forall a. Ord a => t a -> a
     sum :: Num a => t a -> a
     product :: Num a => t a -> a
 }}}

 As a result, the type parameter can be safely constrained. If you plug in
 the code that {{{DeriveFoldable}}} would (hypothetically) generate for
 {{{T}}}:

 {{{#!hs
 {-# LANGUAGE GADTs #-}
 module T where

 data T a where
     MkT :: Int -> T Int

 instance Foldable T where
     foldr f z (MkT a) = f a z
     foldMap f (MkT a) = f a
 }}}

 it compiles without problems, since pattern-matching on the GADT
 constructor specializes {{{foldr}}} to the type {{{(Int -> b -> b) -> b ->
 T Int -> b}}} and {{{foldMap}}} to the type {{{Monoid m => (Int -> m) -> T
 Int -> m}}}.

 Again, this only works with {{{Foldable}}} since the type signatures of
 its methods allow for {{{t a}}} to be refined safely. {{{Functor}}} and
 {{{Traversable}}} do not permit this, so the universal polymorphism check
 would still apply for them.

--
Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/10447#comment:3>
GHC <http://www.haskell.org/ghc/>
The Glasgow Haskell Compiler


More information about the ghc-tickets mailing list