包含带有gdiff的列表的对象的通用差异

时间:2016-12-08 03:18:18

标签: haskell

我正在尝试使用 gdiff 1.1(Haskell的通用差异库)来区分碰巧包含列表的两个对象。但是,我无法让它工作,我认为这是因为我不知道如何为Type FooFamily定义[FooEnvVar]的实例。这是我到目前为止的代码:

module Main where

import Data.Generic.Diff

data Foo = Foo { fooEnv :: [FooEnvVar] }
  deriving (Show, Eq)

data FooStr = FooStr String
  deriving (Show, Eq, Ord)

data FooEnvVar = FooEnvVar { fooEnvName :: FooStr }
  deriving (Show, Eq, Ord)

data FooFamily :: * -> * -> * where
  FooF :: FooFamily Foo (Cons [FooEnvVar] Nil)
  FooStrF :: FooFamily FooStr (Cons String Nil)

instance Family FooFamily where
  decEq FooF FooF = Just (Refl, Refl)
  decEq FooStrF FooStrF = Just (Refl, Refl)
  decEq _ _ = Nothing
  fields FooF (Foo fe) = Just (CCons fe CNil)
  fields FooStrF (FooStr str) = Just (CCons str CNil)
  apply FooF (CCons fe CNil) = Foo fe
  apply FooStrF (CCons str CNil) = FooStr str
  string FooF = "FooF"
  string FooStrF = "FooStrF"

instance Type FooFamily Foo where
  constructors = [Concr FooF]

instance Type FooFamily [FooEnvVar] where
  constructors = []   -- what should I put here?

main :: IO ()
main =
  putStrLn $ show ((diff a b) :: EditScript FooFamily Foo Foo)
  where
    a = Foo [FooEnvVar (FooStr "hello"), FooEnvVar (FooStr "world")]
    b = Foo [FooEnvVar (FooStr "hi"), FooEnvVar (FooStr "world")]

此代码使用-Wall选项在GHC 8.0.1下编译时没有任何警告。当我运行此代码时,我希望它显示ab之间的差异,但它会显示此输出:

test_gdiff: Incorrect Family or Type instance.
CallStack (from HasCallStack):
  error, called at src/Data/Generic/Diff.hs:313:22 in gdiff-1.1-KTbM5AUQcBxD5ewDUGZ4O3:Data.Generic.Diff

如果重要,我使用带有这些扩展的Haskell2010语言:GADT,LambdaCase,MultiParamTypeClasses,OverloadedStrings,FlexibleInstances。

如何解决此错误?

1 个答案:

答案 0 :(得分:2)

gdiff中没有对多态列表的直接支持。

对于您使用列表的每种类型,您必须将列表类型及其构造函数([](:))视为系列的一部分。

以下是适用于您的示例的声明:

data FooFamily :: * -> * -> * where
  FooF       :: FooFamily Foo         (Cons [FooEnvVar] Nil)
  FooEnvVarF :: FooFamily FooEnvVar   (Cons FooStr Nil)
  FooStrF    :: FooFamily FooStr      (Cons String Nil)
  NilF       :: FooFamily [FooEnvVar] Nil
  ConsF      :: FooFamily [FooEnvVar] (Cons FooEnvVar (Cons [FooEnvVar] Nil))
  String     :: String -> FooFamily String Nil

instance Family FooFamily where
  decEq FooF FooF = Just (Refl, Refl)
  decEq FooEnvVarF FooEnvVarF = Just (Refl, Refl)
  decEq FooStrF FooStrF = Just (Refl, Refl)
  decEq NilF NilF = Just (Refl, Refl)
  decEq ConsF ConsF = Just (Refl, Refl)
  decEq (String x) (String y)
    | x == y = Just (Refl, Refl)
    | otherwise = Nothing
  decEq _ _ = Nothing

  fields FooF (Foo fe) = Just (CCons fe CNil)
  fields FooEnvVarF (FooEnvVar ev) = Just (CCons ev CNil)
  fields FooStrF (FooStr x) = Just (CCons x CNil)
  fields NilF [] = Just CNil
  fields ConsF (x : xs) = Just (CCons x (CCons xs CNil))
  fields (String _) _ = Just CNil
  fields _ _ = Nothing

  apply FooF (CCons fe CNil) = Foo fe
  apply FooEnvVarF (CCons ev CNil) = FooEnvVar ev
  apply FooStrF (CCons x CNil) = FooStr x
  apply NilF CNil = []
  apply ConsF (CCons x (CCons xs CNil)) = x : xs
  apply (String x) CNil = x

  string FooF = "FooF"
  string FooEnvVarF = "FooEnvVarF"
  string FooStrF = "FooStrF"
  string NilF = "[]"
  string ConsF = "(:)"
  string (String x) = show x

instance Type FooFamily Foo where
  constructors = [Concr FooF]

instance Type FooFamily [FooEnvVar] where
  constructors = [Concr NilF, Concr ConsF]

instance Type FooFamily FooEnvVar where
  constructors = [Concr FooEnvVarF]

instance Type FooFamily FooStr where
  constructors = [Concr FooStrF]

instance Type FooFamily String where
  constructors = [Abstr String]