How can I derive a Data instance for a GADT in Haskell?

℡╲_俬逩灬. 提交于 2020-01-01 10:16:27

问题


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:


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)
    


来源:https://stackoverflow.com/questions/12573669/how-can-i-derive-a-data-instance-for-a-gadt-in-haskell

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!