It is assumed that instances of GHC.Generic class for data types should be generated automatically by GHC via deriving mechanism, but it is not working for row-types and anonymous records with implicit parameters. So, I am trying to define Generic instance manually and started the experiment with replicating Generic instance for a regular data type using GHCi to get generic representation, as preparation step, but even here I face unexplainable error from the type checker.
> import GHC.Generics
> data Point = Point { x :: Int, y :: Int } deriving (Show, Eq, Generic)
> from (Point 1 2)
M1 {unM1 = M1 {unM1 = M1 {unM1 = K1 {unK1 = 1}} :*: M1 {unM1 = K1 {unK1 = 2}}}}
> :i Rep Point
type instance Rep Point
= D1
(MetaData
"Point"
"RowTypeDemo.ImplicitParamsAsAlternativeToRowType"
"row-type-demo-0.0.1-inplace"
False)
(C1
(MetaCons "Point" PrefixI True)
(S1
(MetaSel
(Just "x") NoSourceUnpackedness NoSourceStrictness DecidedLazy)
(Rec0 Int)
:*: S1
(MetaSel
(Just "y") NoSourceUnpackedness NoSourceStrictness DecidedLazy)
(Rec0 Int)))
Generic instance for Point based on GHCi output above:
data Point = Point { x :: Int, y :: Int } deriving (Show, Eq)
instance Generic Point where
type Rep Point =
D1
(MetaData
"Point"
"RowTypeDemo.ImplicitParamsAsAlternativeToRowType"
"row-type-demo-0.0.1-inplace"
False)
(C1
(MetaCons "Point" PrefixI True)
(S1
(MetaSel
(Just "x") NoSourceUnpackedness NoSourceStrictness DecidedLazy)
(Rec0 Int)
:*: S1
(MetaSel
(Just "y") NoSourceUnpackedness NoSourceStrictness DecidedLazy)
(Rec0 Int)))
from :: Point -> Rep Point x
from r = M1 {unM1 = L1 (M1 {unM1 = M1 {unM1 = K1 {unK1 = x r}} :*: M1 {unM1 = K1 {unK1 = y r}}})}
Type checker fails with:
• Couldn't match type: M1 i0 c0 (M1 i1 c1 (K1 i2 Int) :*: M1 i3 c2 (K1 i4 Int)) :+: g0 with: M1 C (MetaCons "Point" PrefixI True) (S1 (MetaSel (Just "x") NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 Int) :*: S1 (MetaSel (Just "y") NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 Int)) Expected: Rep Point x Actual: M1 D (MetaData "Point" "RowTypeDemo.ImplicitParamsAsAlternativeToRowType" "row-type-demo-0.0.1-inplace" False) (M1 i0 c0 (M1 i1 c1 (K1 i2 Int) :*: M1 i3 c2 (K1 i4 Int)) :+: g0) x • In the expression: M1 {unM1 = L1 (M1 {unM1 = M1 {unM1 = K1 {unK1 = x r}} :*: M1 {unM1 = K1 {unK1 = y r}}})} In an equation for ‘from’: from r = M1 {unM1 = L1 (M1 {unM1 = M1 {unM1 = K1 {unK1 = x r}} :*: M1 {unM1 = K1 {unK1 = y r}}})} In the instance declaration for ‘Generic Point’ | 87 | from r = M1 {unM1 = L1 (M1 {unM1 = M1 {unM1 = K1 {unK1 = x r}} :*: M1 {unM1
= K1 {unK1 = y r}}})} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
I don't see where the problem is, because type family application Rep Point x
should be equal to its value.
GHC version is 9.8.1
List of enabled extensions:
DataKinds LambdaCase ImplicitParams OverloadedLabels OverloadedStrings TupleSections TypeFamilies UnicodeSyntax GADTs PolyKinds RankNTypes TypeOperators TypeApplications
Update:
Kudos to @leftaroundabout. GHCi generates wrong representation.
Following implementations satisfy the type checker.
from :: Point -> Rep Point x
from r = M1 {unM1 = (M1 {unM1 = M1 {unM1 = K1 {unK1 = x r}} :*: M1 {unM1 = K1 {unK1 = y r}}})}
to (M1 {unM1 = (M1 {unM1 = M1 {unM1 = K1 {unK1 = xx}} :*: M1 {unM1 = K1 {unK1 = yy}}})}) = Point xx yy
Update 2
It was my fault not GHC one. I messed somehow and used sum type representation.
You have a sporadic L1
in your definition. That's a constructor for :+:
, which doesn't make sense for your type.
This definition compiles:
from r = M1 {unM1 = M1 {unM1 = M1 {unM1 = K1 {unK1 = x r}} :*: M1 {unM1 = K1 {unK1 = y r}}}}
or, more readably,
from r = M1 . M1 $ M1 (K1 $ x r) :*: M1 (K1 $ y r)
I rather doubt writing such an instance is sensible, though. Generally the whole point of a generic instance is that it allows you to define class instances without needing to write special code dealing with the structure of your type. If you're going to write such code anyway, why not just instantiate the class of interest right away? If your type is unusual enough to not support a derived Generic
instance, then this would likely be more appropriate.