如何在Haskell中为GADT派生数据实例?

a1k*_*kmm 5 haskell gadt deriving

我有一个只用过两个不同参数的GADT,ForwardPossible和():

-- | 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)
Run Code Online (Sandbox Code Playgroud)

我想派生足够的Data.Data实例来覆盖OrForward t()和OrForward t ForwardPossible.我不认为一般(数据t,数据转发)=> OrForward t forward实例是可能的,除非它普遍忽略OFForward,但是数据的重叠实例t => OrForward t ForwardPossible和(Data t,Data forward)=>如果有一种方法可以使ghc派生出那些实例,那么OrForward t forward实例可能是一个解决方案.

我试过定义:

deriving instance Data t => Data (OrForward t ())
deriving instance Data t => Data (OrForward t ForwardPossible)
Run Code Online (Sandbox Code Playgroud)

但是ghc给我一个这样的错误:

Duplicate type signature:
  Structure.hs:53:1-70: $tOrForward :: DataType
  Structure.hs:52:1-49: $tOrForward :: DataType
Run Code Online (Sandbox Code Playgroud)

a1k*_*kmm 3

我发现了一种相当不干净的方法来解决这个问题,所以我将其放在这里,以防其他人找到更好的答案:

  1. 我在主结构模块之上创建了两个新模块,专门用于派生实例。我使用了一种方法来派生采用 ForwardPossible 的 GADT 专业化实例,另一种方法用于采用 () 的实例,利用 StandaloneDeriving 和FlexibleInstances。这避免了 ghc 添加的代码中通过将它们放在不同的模块中来实现 Data.Data 的内部符号冲突的问题。

  2. 我必须手动编写实例 Data t => Data (OrForward t ()) 以排除 OFForward 情况:

    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
    
    Run Code Online (Sandbox Code Playgroud)
  3. Data t => Data (OrForward t ForwardPossible) 的实例可以导出:

    deriving instance Data t => Data (OrForward t ForwardPossible)
    
    Run Code Online (Sandbox Code Playgroud)