[GHC] #8516: Add (->) representation and the Invariant class to GHC.Generics
GHC
ghc-devs at haskell.org
Wed Dec 23 18:10:22 UTC 2015
#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:
Operating System: Unknown/Multiple | Architecture:
| Unknown/Multiple
Type of failure: None/Unknown | Test Case:
Blocked By: | Blocking:
Related Tickets: | Differential Rev(s):
Wiki Page: |
-------------------------------------+-------------------------------------
Comment (by RyanGlScott):
Well, I had previously thought one could get away with not considering
`Invariant`, but after thinking about it some more, having `Invariant` is
crucial for this new feature to be compositional.
We'd add two new data types:
{{{#!hs
infixr 4 :->:, :->-:
newtype (f :->: r) p = ContraArrow1 { unContraArrow1 :: f p -> r }
newtype (f :->-: g) p = InvArrow1 { unInvArrow1 :: f p -> g p }
}}}
And, if we [https://ghc.haskell.org/trac/ghc/ticket/7492 change] `Rec1 f`
to `f :.: Par1`, then we can also define the following for symmetry:
{{{#!hs
infixr 4 :>-:
type (r :>-: f) = ((->) r) :.: f
}}}
(I've adopted a pretty arbitrary naming scheme where hyphens (`-`) denote
occurrences of the type parameter. Feel free to suggest other names.)
Then we can generate instances for data types no matter which side of an
arrow a type parameter might occur. Here is an example:
{{{#!hs
newtype Endo a = Endo (a -> a) deriving Generic1
instance Invariant Endo where
invmap f g (Endo x) = Endo (f . x . g)
==>
instance Generic1 Endo where
type Rep1 Endo = D1 ... (C1 ... (S1 ... (Par1 :->-: Par1)))
to1 (M1 (M1 (M1 c))) = Endo ((.) (invCompose unPar1 Par1) unInvArrow1 c)
from1 (Endo c) = M1 (M1 (M1 ((.) InvArrow1 (invCompose Par1 unPar1) c)))
invCompose :: (c -> d) -> (a -> b) -> (b -> c) -> a -> d
invCompose = \f g h x -> f (h (g x))
}}}
So far, so good. But if we define something like this:
{{{#!hs
newtype Endo2 a = Endo2 (Endo a) deriving Generic1
}}}
Then things become awkward. GHC would attempt to generate an instance like
this:
{{{#!hs
instance Generic1 Endo2 where
type Rep1 Endo2 = D1 ... (C1 ... (S1 ... (Endo :.: Par1)))
to1 (M1 (M1 (M1 c))) = Endo2 ((.) (fmap unPar1) unComp1 c)
from1 (Endo2 c) = M1 (M1 (M1 ((.) Comp1 (fmap Par1) c)))
}}}
But this will never work, because it assumes `Endo` is a `Functor`
instance, which can't happen. This is quite a problem: we can make one
datatype a `Generic1` instance, but we can't make a simple `newtype`
wrapper around it a `Generic1` instance!
However, if we changed the way GHC generated code for the `:.:` case to
assume that the outermost datatype is an `Invariant` instance, not a
`Functor` instance, then it would work:
{{{#!hs
instance Generic1 Endo where
type Rep1 Endo = D1 ... (C1 ... (S1 ... (Par1 :->-: Par1)))
to1 (M1 (M1 (M1 c))) = Endo ((.) (invCompose unPar1 Par1) unInvArrow1 c)
from1 (Endo c) = M1 (M1 (M1 ((.) InvArrow1 (invCompose Par1 unPar1) c)))
}}}
Of course, this would mean bringing in `Invariant` to `base`, and it makes
an assumption that most datatypes you'd find in the wild are `Invariant`
instances already. As I mentioned above, trying to make `Invariant` a
superclass of `Functor` is a hard sell.
Futhermore, I'm not sure if we'd really gain anything after all this
breakage besides being able to derive `Functor` and `Invariant` instances
generically. I'm not convinced the benefits outweigh the potential
heartburn in this case.
--
Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/8516#comment:3>
GHC <http://www.haskell.org/ghc/>
The Glasgow Haskell Compiler
More information about the ghc-tickets
mailing list