How can I derive a Data instance for a GADT in Has

2019-04-09 06:49发布

I have a GADT which is only ever used with two different parameters, ForwardPossible and ():

-- | Used when a forward definition is possible.
data ForwardPossible = ForwardPossible deriving (Eq, Ord, Typeable, Data, Show)

-- | GADT which accepts forward definitions if parameter is ForwardPossible.
data OrForward t forward where
  OFKnown :: t -> OrForward t forward
  OFForward :: NamespaceID -> SrcSpan -> BS.ByteString -> OrForward t ForwardPossible

deriving instance Eq t => Eq (OrForward t forward)
deriving instance Ord t => Ord (OrForward t forward)
deriving instance Typeable2 OrForward
deriving instance Show t => Show (OrForward t forward)

I would like to derive enough Data.Data instances to cover both OrForward t () and OrForward t ForwardPossible. I don't think a general (Data t, Data forward) => OrForward t forward instance is possible unless it universally ignores OFForward, but either overlapping instances for Data t => OrForward t ForwardPossible and (Data t, Data forward) => OrForward t forward instances could be a solution if there is a way to make ghc derive those instances.

I have tried defining:

deriving instance Data t => Data (OrForward t ())
deriving instance Data t => Data (OrForward t ForwardPossible)

but then ghc gives me an error like this:

Duplicate type signature:
  Structure.hs:53:1-70: $tOrForward :: DataType
  Structure.hs:52:1-49: $tOrForward :: DataType

1条回答
一夜七次
2楼-- · 2019-04-09 07:26

I found a rather unclean way to work around the problem, so I'll put it here in case no one else finds a better answer:

  1. I created two new modules on top of the main Structure module specifically for deriving instances. I used one to derive instances the GADT specialisation taking ForwardPossible, and one for the instance taking (), making use of StandaloneDeriving and FlexibleInstances. This avoided the issue of conflicting internal symbols from the code added by ghc to implement Data.Data by putting them in different modules.

  2. I had to write instance Data t => Data (OrForward t ()) manually to exclude the OFForward case:

    instance Data t => Data (OrForward t ()) where
      gfoldl k z (OFKnown a1) = (z OFKnown `k` a1)
      gunfold k z c = case constrIndex c of
      _ -> k (z OFKnown)
      toConstr _ = cOFKnown
      dataTypeOf _ = tOrForward
      dataCast2 f = gcast2 f
    
    tOrForward :: Data.Data.DataType
    tOrForward =
      mkDataType
        "Data.FieldML.Structure.OrForward"
        [cOFKnown]
    
    cOFKnown :: Data.Data.Constr
    cOFKnown = mkConstr tOrForward
                 "OFKnown" [] Prefix
    
  3. The instance for Data t => Data (OrForward t ForwardPossible) could be derived:

    deriving instance Data t => Data (OrForward t ForwardPossible)
    
查看更多
登录 后发表回答