[GHC] #8516: Add (->) representation and the Invariant class to GHC.Generics
GHC
ghc-devs at haskell.org
Sun Aug 14 18:07:05 UTC 2016
#8516: Add (->) representation and the Invariant class to GHC.Generics
-------------------------------------+-------------------------------------
Reporter: nfrisby | Owner:
Type: feature request | Status: new
Priority: low | Milestone:
Component: Compiler (Type | Version: 7.7
checker) |
Resolution: | Keywords: Generics
Operating System: Unknown/Multiple | Architecture:
| Unknown/Multiple
Type of failure: None/Unknown | Test Case:
Blocked By: | Blocking:
Related Tickets: | Differential Rev(s):
Wiki Page: |
-------------------------------------+-------------------------------------
Changes (by RyanGlScott):
* cc: goldfire (added)
Comment:
I've been thinking about this recently, and I suspect there might be a way
to accomplish this without needing anything like `Functor` or `Invariant`.
The //only// reason we currently use `Functor` constraints in derived
`Generic1` instances is to give us the ability to "tunnel into" data
structures that are polymorphically recursive at least two levels deep
(e.g., `newtype Compose f g a = Compose (f (g a))`). Consider a `Generic1`
instance for `Foo`:
{{{#!hs
instance Functor f => Generic1 (Compose f g) where
type Rep1 (Compose f g) =
D1 ('MetaData "Compose" "Ghci3" "interactive" 'True)
(C1 ('MetaCons "Compose" 'PrefixI 'False)
(S1 ('MetaSel 'Nothing 'NoSourceUnpackedness 'NoSourceStrictness
'DecidedLazy)
(f :.: Rec1 g)))
from1 (Compose x) = M1 (M1 (M1 (Comp1 (fmap Rec1 x))))
to1 (M1 (M1 (M1 x))) = Compose (fmap unRec1 (unComp1 x))
}}}
This works, but requires that ugly `Functor` constraint. Is there an
alternative? If we replace the `fmap` calls with holes:
{{{#!hs
instance Generic1 (Compose f g) where
type Rep1 (Compose f g) =
D1 ('MetaData "Compose" "Ghci3" "interactive" 'True)
(C1 ('MetaCons "Compose" 'PrefixI 'False)
(S1 ('MetaSel 'Nothing 'NoSourceUnpackedness 'NoSourceStrictness
'DecidedLazy)
(f :.: Rec1 g)))
from1 (Compose x) = M1 (M1 (M1 (Comp1 (_1 x))))
to1 (M1 (M1 (M1 x))) = Compose (_2 (unComp1 x))
}}}
Then it becomes clear what functions we need:
{{{#!hs
_1 :: f (g a) -> f (Rec1 g a)
_2 :: f (Rec1 g a) -> f (g a)
}}}
All that `_1` and `_2` are doing is wrapping and unwrapping a `newtype`
underneath an `f`! This would be a perfect job for `Data.Coerce.coerce`.
If we could write something like this:
{{{#!hs
instance (Coercible (f (g a)) (f (Rec1 g a)), Coercible (f (Rec1 g a)) (f
(g a))
=> Generic1 (Compose f g) where
type Rep1 (Compose f g) =
D1 ('MetaData "Compose" "Ghci3" "interactive" 'True)
(C1 ('MetaCons "Compose" 'PrefixI 'False)
(S1 ('MetaSel 'Nothing 'NoSourceUnpackedness 'NoSourceStrictness
'DecidedLazy)
(f :.: Rec1 g)))
from1 (Compose x) = M1 (M1 (M1 (Comp1 (coerce x))))
to1 (M1 (M1 (M1 x))) = Compose (coerce (unComp1 x))
}}}
It would work! But we can't write this since `a` isn't in scope in the
instance's context. I can't even figure out how to hack it using something
like [http://hackage.haskell.org/package/constraints-0.8/docs/Data-
Constraint-Forall.html Data.Constraint.Forall] from the `constraints`
library.
Of course, there's the issue that we don't know //a priori// what role the
type argument to `f` has, so there would still be some types for which
this `Generic1` instance wouldn't typecheck. But I claim that any type for
which `f` is forced to be nominal (i.e., for which you couldn't `coerce`
underneath `f`) couldn't have a `Functor` instance anyway, so this
approach should be no less expressive than the current one.
goldfire, would #9123 (higher-kinded roles) give us the ability to express
this? If so, I think we could sidestep the issue of including `Invariant`
in `base` entirely, since it would become unnecessary.
--
Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/8516#comment:5>
GHC <http://www.haskell.org/ghc/>
The Glasgow Haskell Compiler
More information about the ghc-tickets
mailing list