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

1

6

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
Pareira answered 24/9, 2012 at 22:12 Comment(1)
As a small note, if you're using GHC 7.4 or 7.6 with DataKinds, you can have data OrForward t (forward :: Bool) and then your two types are OrForward t True and OrForward t False (or of course you can make your own 2 type and use that instead of Bool).Elise
P
3

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)
    
Pareira answered 24/9, 2012 at 23:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.