[GHC] #13327: Figure out how to make sense of Data instances for poly-kinded datatypes
GHC
ghc-devs at haskell.org
Thu Feb 23 19:38:40 UTC 2017
#13327: Figure out how to make sense of Data instances for poly-kinded datatypes
-------------------------------------+-------------------------------------
Reporter: RyanGlScott | Owner: (none)
Type: bug | Status: new
Priority: normal | Milestone:
Component: | Version: 8.0.1
libraries/base |
Keywords: | Operating System: Unknown/Multiple
Architecture: | Type of failure: None/Unknown
Unknown/Multiple |
Test Case: | Blocked By:
Blocking: | Related Tickets: #4028
Differential Rev(s): | Wiki Page:
-------------------------------------+-------------------------------------
Something's funny with `Data.Data`. In particular, what instance do you
expect when you write this?
{{{#!hs
data T phantom = T
deriving Data
}}}
You might expect:
{{{#!hs
instance Typeable phantom => Data (T phantom) where
...
}}}
And indeed, it is possible to define a `Data` instance like this. But in
practice, GHC actually derives this instance:
{{{#!hs
instance Data phantom => Data (T phantom) where
...
dataCast1 f = gcast1 f
}}}
The reason is because GHC has special cases for when it derives `Data`
instances for datatypes of kind `* -> *` or `* -> * -> *`. These special
cases cause the derived `Data` instances to insert definitions for
`dataCast1` or `dataCast2`, respectively, and their implementations
(`gcast1` or `gcast2`, respectively) require the stronger instance
context. See #4028 for a longer discussion about this.
Things get far less predictable when you throw in `PolyKinds`, however. If
you try this instead:
{{{#!hs
data T (phantom :: k) = T
deriving Data
}}}
Then you do //not// get `instance Data phantom => Data (T phantom)` as
above. Instead, you get this:
{{{#!hs
instance (Typeable k, Typeable (phantom :: k)) => Data (T phantom) where
...
-- No implementation for dataCast1
}}}
As it turns out, GHC's special-casing for deriving `Data` is not privy to
datatypes of kind `k -> *` or `k1 -> k2 -> *`, just `* -> *` and `* -> *
-> *`.
This is all a bit disconcerting because if you look at `Data.Data`, there
are many instances of the flavor:
{{{#!hs
deriving instance Data (p :: *) => Data (U1 p) -- U1 :: k -> *
}}}
This makes sense in a pre-`PolyKinds` world, but really, the most general
type for this `Data` instance would be:
{{{#!hs
deriving instance (Typeable k, Typeable (p :: k)) => Data (U1 p)
}}}
Even worse, choosing the less polymorphic instance `Data (p :: *) => Data
(U1 p)` via `StandaloneDeriving` doesn't even cause GHC to emit a
definition for `dataCast1`—it just restricts the kind with no benefit!
This whole situation doesn't sit well with me, especially since as we add
more poly-kinded `Data` instances to `Data.Data`, we have to ask ourselves
each time what the "right" `Data` instance is. It would be great if we
could answer these questions:
1. Should we expand the special-casing for datatypes of kind `k -> *` or
`k1 -> k2 -> *`? Or do `dataCast1` and `dataCast2` even make sense for
poly-kinded datatypes?
2. Is there a way to modify `DeriveDataTypeable` such that emitting
definitions for `dataCast1` and `dataCast2` don't require us to default
some kind variables to `*`? Perhaps `TypeInType` could help us here
somehow.
--
Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/13327>
GHC <http://www.haskell.org/ghc/>
The Glasgow Haskell Compiler
More information about the ghc-tickets
mailing list