[GHC] #10343: Make Typeable track kind information better
GHC
ghc-devs at haskell.org
Thu Apr 23 16:02:37 UTC 2015
#10343: Make Typeable track kind information better
-------------------------------------+-------------------------------------
Reporter: oerjan | Owner:
Type: feature request | Status: new
Priority: normal | Milestone:
Component: Compiler | Version: 7.10.1
Resolution: | Keywords:
Operating System: Unknown/Multiple | Architecture:
Type of failure: None/Unknown | Unknown/Multiple
Blocked By: | Test Case: typeOf ::
Related Tickets: #9858 | Typeable (a::k) => Proxy a ->
| TypeRep
| Blocking:
| Differential Revisions:
-------------------------------------+-------------------------------------
Description changed by oerjan:
Old description:
> (EDIT: My long rambling seems to have been confusing, so a summary:
>
> I want that the type inference and runtime representation should support
> constructing Typeable (T :: k), even when k contains kind variables that
> are only mentioned in other Typeable constraints not necessarily
> involving the type constructor T. This will allow simple composition
> with polymorphic type constructors like `Proxy` and `Typeable` to work
> more naturally, but also more complicated things.)
>
> With all the apparent exploitable bugs now fixed, #9858 seems to be
> settling down. Simon P.J. asked me to make a new ticket for some
> quibbles I still have with limitations of the new design. These started
> with some examples that I discussed there pre-implementation with Richard
> Eisenberg, and which he concluded could be supported by a "sufficently
> smart solver".
>
> In the new (current) design, a `TypeRep` ''does'' contain kind
> information, which as I understand it is a list of `KindRep`s for the
> explicit kind parameters applied to the type constructor after type
> inference. This is sufficient to prevent confusing the same type
> constructor used at different kinds, and also easily supports type
> application ''provided'' the `TypeRep`s of the parts are known. For this,
> the internal `Typeable` constraint solver only ever needs to combine
> `TypeRep`s, never decompose them.
>
> Since no one else complained in #9858, I assume this is enough for what
> people are currently using `Typeable` for. However:
>
> I think the current `TypeRep` representation for `Typeable (a :: k)`
> doesn't contain enough information to generally extract the `KindRep` for
> `k` itself, much less to decompose it, even if the internal `Typeable`
> solver were to try. This prevents some polykinded code that is
> intuitively sound, and which "worked" in the old system, although if only
> because it didn't try to track kinds at all.
>
> For example, the following expression, with `PolyKinds` enabled:
>
> {{{#!hs
> typeOf :: Typeable a => Proxy a -> TypeRep
> }}}
>
> In the old system, this worked simply by combining the `TypeRep` for
> `Proxy` with the `TypeRep` for `a`. But in the new system, it fails,
> because the `TypeRep` for `Proxy` needs to know the kind of `a`, which is
> neither statically known nor available from the `TypeRep` of `a`. You
> would need to have compile-time information about the type constructor of
> `a` to even know how to build the `KindRep` from the kind arguments.
>
> This problem with constructing `TypeRep`s would arise whenever a
> polykinded type constructor is applied to a type whose kind is not a
> known monomorphic one. The second similar one I can think of is
>
> {{{#!hs
> typeRep :: Typeable a => Proxy (Typeable a) -> TypeRep
> }}}
>
> In an ideal system, we can also find use cases that require not just
> extracting the `KindRep` for a type, but also decomposing it. Here is my
> very hypothetical kitchen sink example from [comment:46:ticket:9858]
> (this example was made up as a test case before I knew what the new
> solver would support):
>
> {{{#!hs
> {-# LANGUAGE PolyKinds #-}
> {-# LANGUAGE DataKinds #-}
> {-# LANGUAGE KindSignatures #-}
> {-# LANGUAGE AutoDeriveTypeable #-}
>
> import Data.Typeable
>
> newtype C a b = C ()
>
> step :: Proxy (a :: [k] -> *) -> Proxy (C a :: [k -> *] -> *)
> step Proxy = Proxy
>
> nest :: Typeable (a :: [k] -> *) => Int -> Proxy a -> TypeRep
> nest 0 x = typeRep x
> nest n x = nest (n-1) (step x)
>
> main = print $ nest 10 (Proxy :: Proxy (C () :: [()] -> *))
> }}}
>
> Here, to make this compile, the solver would need to derive `Typeable (C
> a :: [k -> *] -> *]` from `Typeable (a :: [k] -> *)`, which at runtime
> requires first extracting the `KindRep` of `k` from the `TypeRep` of `a`,
> and then building the `TypeRep` for the correctly kinded `C`. At least
> the former part seems essentially impossible with the current `TypeRep`
> representation, even with a more clever solver.
New description:
(EDIT: My long rambling seems to have been confusing, so a summary:
I think that the type inference and runtime representation should support
constructing `Typeable (T :: k)`, even when `k` contains kind variables
that are only mentioned in other `Typeable` constraints not necessarily
involving the type constructor `T`. This will allow simple composition
with polymorphic type constructors like `Proxy` and `Typeable` to work
more naturally, but also more complicated things.)
With all the apparent exploitable bugs now fixed, #9858 seems to be
settling down. Simon P.J. asked me to make a new ticket for some quibbles
I still have with limitations of the new design. These started with some
examples that I discussed there pre-implementation with Richard Eisenberg,
and which he concluded could be supported by a "sufficently smart solver".
In the new (current) design, a `TypeRep` ''does'' contain kind
information, which as I understand it is a list of `KindRep`s for the
explicit kind parameters applied to the type constructor after type
inference. This is sufficient to prevent confusing the same type
constructor used at different kinds, and also easily supports type
application ''provided'' the `TypeRep`s of the parts are known. For this,
the internal `Typeable` constraint solver only ever needs to combine
`TypeRep`s, never decompose them.
Since no one else complained in #9858, I assume this is enough for what
people are currently using `Typeable` for. However:
I think the current `TypeRep` representation for `Typeable (a :: k)`
doesn't contain enough information to generally extract the `KindRep` for
`k` itself, much less to decompose it, even if the internal `Typeable`
solver were to try. This prevents some polykinded code that is
intuitively sound, and which "worked" in the old system, although if only
because it didn't try to track kinds at all.
For example, the following expression, with `PolyKinds` enabled:
{{{#!hs
typeOf :: Typeable a => Proxy a -> TypeRep
}}}
In the old system, this worked simply by combining the `TypeRep` for
`Proxy` with the `TypeRep` for `a`. But in the new system, it fails,
because the `TypeRep` for `Proxy` needs to know the kind of `a`, which is
neither statically known nor available from the `TypeRep` of `a`. You
would need to have compile-time information about the type constructor of
`a` to even know how to build the `KindRep` from the kind arguments.
This problem with constructing `TypeRep`s would arise whenever a
polykinded type constructor is applied to a type whose kind is not a known
monomorphic one. The second similar one I can think of is
{{{#!hs
typeRep :: Typeable a => Proxy (Typeable a) -> TypeRep
}}}
In an ideal system, we can also find use cases that require not just
extracting the `KindRep` for a type, but also decomposing it. Here is my
very hypothetical kitchen sink example from [comment:46:ticket:9858] (this
example was made up as a test case before I knew what the new solver would
support):
{{{#!hs
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE AutoDeriveTypeable #-}
import Data.Typeable
newtype C a b = C ()
step :: Proxy (a :: [k] -> *) -> Proxy (C a :: [k -> *] -> *)
step Proxy = Proxy
nest :: Typeable (a :: [k] -> *) => Int -> Proxy a -> TypeRep
nest 0 x = typeRep x
nest n x = nest (n-1) (step x)
main = print $ nest 10 (Proxy :: Proxy (C () :: [()] -> *))
}}}
Here, to make this compile, the solver would need to derive `Typeable (C a
:: [k -> *] -> *]` from `Typeable (a :: [k] -> *)`, which at runtime
requires first extracting the `KindRep` of `k` from the `TypeRep` of `a`,
and then building the `TypeRep` for the correctly kinded `C`. At least
the former part seems essentially impossible with the current `TypeRep`
representation, even with a more clever solver.
--
--
Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/10343#comment:11>
GHC <http://www.haskell.org/ghc/>
The Glasgow Haskell Compiler
More information about the ghc-tickets
mailing list