Opened 9 years ago

Closed 8 years ago

Last modified 2 years ago

#4028 closed bug (wontfix)

Derived Data instance requires Data instances for unused type parameters

Reported by: igloo Owned by:
Priority: normal Milestone: Not GHC
Component: Compiler (Type checker) Version: 6.12.2
Keywords: deriving Cc: alfonso.acosta@…, HoseinAttarzadeh, leather@…, dreixel
Operating System: Unknown/Multiple Architecture: Unknown/Multiple
Type of failure: None/Unknown Test Case:
Blocked By: Blocking:
Related Tickets: Differential Rev(s):
Wiki Page:

Description

First reported here: http://www.haskell.org/pipermail/glasgow-haskell-users/2010-April/018785.html

With this module:

{-# LANGUAGE DeriveDataTypeable #-}

import Data.Data
import Data.Typeable

newtype FSVec s a = FSVec {unFSVec :: [a]}
    deriving (Eq, Typeable, Data)

6.12 now has a Data s requirement on the Data (FSVec s a) instance.

With 6.10.4:

*Main> :i FSVec
newtype FSVec s a = FSVec {unFSVec :: [a]}
        -- Defined at q.hs:7:8-12
instance (Eq a) => Eq (FSVec s a) -- Defined at q.hs:8:14-15
instance (Data a, Typeable s) => Data (FSVec s a)
  -- Defined at q.hs:8:28-31
instance Typeable2 FSVec -- Defined at q.hs:8:18-25

With 6.12 (and the HEAD):

*Main> :i FSVec
newtype FSVec s a = FSVec {unFSVec :: [a]}
        -- Defined at q.hs:7:8-12
instance (Eq a) => Eq (FSVec s a) -- Defined at q.hs:8:14-15
instance (Data s, Data a) => Data (FSVec s a)
  -- Defined at q.hs:8:28-31
instance Typeable2 FSVec -- Defined at q.hs:8:18-25

Change History (8)

comment:1 Changed 9 years ago by fons

Cc: alfonso.acosta@… added

comment:2 Changed 9 years ago by fons

This has an impact on packet paramterized-data and unfortunately as a consequence is a major blocker for upgrading the ForSyDe packet.

comment:3 Changed 9 years ago by HoseinAttarzadeh

Cc: HoseinAttarzadeh added

comment:4 Changed 9 years ago by simonpj

Cc: leather@… dreixel added
Milestone: 6.14.1Not GHC

I've had a chance to look at this. Sadly, 6.10 is wrong here; the change was the fix to #3087. The derived instance looks like this, as you'll see if you do -ddump-deriv:

instance (Data s, Data a) => Data (FSVec s a) where 
    dataCast2 f           = Data.Typeable.gcast2 f    -- <-------  NOTA BENE 
    dataTypeOf _          = tFSVec
    toConstr (FSVec _)    = cFSVec
    gunfold k z _         = k (z FSVec)
    gfoldl k z (FSVec a1) = (z FSVec `k` a1)
cFSVec = Data.Data.mkConstr tFSVec "FSVec" ["unFSVec"] Data.Data.Prefix
tFSVec = Data.Data.mkDataType "T4028.FSVec" [cFSVec]

The new bit, compared to 6.10, is the definition of dataCast2. It is defined in the Data class thus

class Data a where 
  dataCast2 :: Typeable2 t
            => (forall d e. (Data d, Data e) => c (t d e))
            -> Maybe (c a)

and gcast2 has this type:

gcast2 :: forall t,t',a,b.  (Typeable2 t, Typeable2 t') 
       => c (t a b) -> Maybe (c (t' a b)) 

Now to typecheck the instance

instance (Data a, Data s) => Data (FSVec s a) where
   dataCast2 f = gcast2 f

we need that (Data a, Data s) context. Try that exact instance declaration and you'll see. And there you are.

If you want to know about dataCast2 it's all in Section 7 of the SYB2 paper http://research.microsoft.com/en-us/um/people/simonpj/papers/hmap/gmap2.ps.

If you don't need dataCast2 you can define it to return Nothing:

  dataCast2 f = Nothing

but you can't use deriving for that; you'd have to write out the derived instance yourself.

I'm not sure how to help you here. I can see that since 's' is phantom it seems odd to require (Data s) but that's what the type checker will require. My brain has forgotten the details of dataCast2 and friends.

Maybe some generic programming experts (at Utrecht, for example) might help? I'm adding Sean Leather to the cc list.

I'll leave this open, but not because there is a bug as specified; rather maybe there is a better design of the Data library waiting to be discovered. I'll milestone it as 'Not GHC' though.

I suppose another design would be for derived 'Data' not to define dataCast1,2, but #3087 specifically asked for them to be so!

Simon

comment:5 Changed 9 years ago by dreixel

Consider the following different choices of Data instances for FSVec:

newtype FSVec s a = FSVec {unFSVec :: [a]} deriving (Eq, Typeable)
instance (Typeable s, Data a) => Data (FSVec s a)

newtype FSVec1 s a = FSVec1 {unFSVec1 :: [a]} deriving (Eq, Typeable)
instance (Typeable s, Data a) => Data (FSVec1 s a) where
    dataCast1 f            = Data.Typeable.gcast1 f
    
newtype FSVec2 s a = FSVec2 {unFSVec2 :: [a]} deriving (Eq, Typeable)
instance (Data s, Data a) => Data (FSVec2 s a) where
    dataCast2 f            = Data.Typeable.gcast2 f

Let's now test their behavior when extending a generic function with an adhoc case:

-- We need to define ext2Q, which uses dataCast2.
-- ext1Q, which is defined in the library, uses dataCast1. 
newtype Q r a = Q { unQ :: a -> r }
ext2Q :: (Data d, Typeable2 t)
      => (d -> q) -> (forall d1 d2. (Data d1, Data d2) => t d1 d2 -> q) -> d -> q
ext2Q def ext arg =
  case dataCast2 (Q ext) of
    Just (Q ext') -> ext' arg
    Nothing       -> def arg


test, test1, test2 :: Char
test  = (const 'p') `ext2Q` (\(FSVec  _) -> 'q') $ (FSVec  "" :: FSVec  () Char)
test1 = (const 'p') `ext2Q` (\(FSVec1 _) -> 'q') $ (FSVec1 "" :: FSVec1 () Char)
test2 = (const 'p') `ext2Q` (\(FSVec2 _) -> 'q') $ (FSVec2 "" :: FSVec2 () Char)

main = print (test, test1, test2)

From my perspective, only test3 behaves correctly, and hence we should define dataCast2 for FSVec as ghc 6.12 does. Note also that FSVec is an instance of Typeable2, so it's only sensible to use dataCast2 for it.

comment:6 in reply to:  5 Changed 9 years ago by dreixel

The output of main above is:

('p','p','q')

comment:7 Changed 8 years ago by igloo

Resolution: wontfix
Status: newclosed

dreixel says that he thinks nothing should be done, and no dissenters, so closing this ticket.

comment:8 Changed 2 years ago by RyanGlScott

Keywords: deriving added
Note: See TracTickets for help on using tickets.