[GHC] #9123: Need for higher kinded roles

GHC ghc-devs at haskell.org
Fri Dec 12 14:14:31 UTC 2014


#9123: Need for higher kinded roles
-------------------------------------+-------------------------------------
              Reporter:  simonpj     |            Owner:  goldfire
                  Type:  bug         |           Status:  new
              Priority:  normal      |        Milestone:  7.12.1
             Component:  Compiler    |          Version:  7.8.2
            Resolution:              |         Keywords:
      Operating System:              |     Architecture:  Unknown/Multiple
  Unknown/Multiple                   |       Difficulty:  Project (more
       Type of failure:              |  than a week)
  None/Unknown                       |       Blocked By:
             Test Case:              |  Related Tickets:
              Blocking:              |
Differential Revisions:              |
-------------------------------------+-------------------------------------

Comment (by goldfire):

 I've recently realized that an alternate, much more tantalizing fix to
 this problem is to allow quantifying over implication constraints (#2256).

 To be concrete, consider

 {{{#!hs
 class Monad m where
   join :: m (m a) -> m a

 data StateT s m a = StateT (s -> m (a, s))   -- could be a newtype, but
 that doesn't change my argument
 type role StateT nominal representational nominal   -- as inferred
 instance Monad m => Monad (StateT s m) where ...

 newtype IntStateT m a = IntStateT (StateT Int m a)
   deriving Monad
 }}}

 This induces the following instance:

 {{{
 instance (...) => Monad (IntStateT m) where
   join = coerce (join :: StateT Int m (StateT Int m a) -> StateT Int m a)
            :: forall a. IntStateT m (IntStateT m a) -> IntStateT m a
 }}}

 GHC needs to infer the `(...)`. It starts with `Monad m` along with

 {{{
 Coercible (StateT Int m (StateT Int m a) -> StateT Int m a) (IntStateT m
 (IntStateT m a) -> IntStateT m a)
 }}}

 This gets reduced to

 {{{
 Coercible (StateT Int m (StateT Int m a)) (StateT Int m (IntStateT m a))
 }}}

 and is then stuck, unable to unwrap the newtype in a nominal context. The
 problem, at this point is that `a` is out of scope in the instance context
 `(...)`, and so GHC gives up. If we had implication constraints, we could
 just quantify! The instance context would be complex, but everything would
 work out in practice.

 As a separate case, imagine I had declared `StateT` as a newtype (so that
 we can look through to its definition) and we wanted to prove, say,
 `Coercible (StateT Int) (StateT Age)`. Right now, we're stuck, but, I
 conjecture that the following would work:

 {{{
 data Coercion a b where Coercion :: Coercible a b => Coercion a b
 pf :: forall m c. (forall a b. Coercible a b => Coercible (m a) (m b)) =>
 Coercion (StateT Int m c) (StateT Age m c)
 pf = Coercion
 }}}

 (The `c` parameter is necessary to allow GHC to unwrap the newtype, as it
 won't unwrap a partially-applied newtype.)

 To prove, GHC unwraps the newtypes to get

 {{{
 [W] Coercible (Int -> m (c, Int)) (Age -> m (c, Age))
 }}}

 and then

 {{{
 [W] Coercible (m (c, Int)) (m (c, Age))
 }}}

 GHC will then look for an appropriate instance, finding

 {{{
 [G] forall a b. Coercible a b => Coercible (m a) (m b)
 }}}

 which matches nicely. (This last step is beyond the current algorithm for
 the treatment of givens, but it's exactly what the instance-lookup
 algorithm does! So we just adapt that to a new scenario.) Thus, we now
 have

 {{{
 [W] Coercible (c, Int) (c, Age)
 }}}

 which is solved handily.

 More comments on the solving algorithm on #2256, shortly.

 I think this solution, of using implication constraints, is '''much'''
 better than the ad-hoc idea in [wiki:Roles2]. It's more powerful, simpler
 to explain, simpler to implement, and it goes "all the way".

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


More information about the ghc-tickets mailing list