Haskell:为包含在另一种类型中的类型使用正确的实例

Edw*_*ley 5 haskell types

假设我们有以下代码,其中有两种类型最终放在另外两种类型中,其中最外面的是GADT:

{-# LANGUAGE FlexibleInstances,
             GADTSyntax,
             GADTs,
             OverlappingInstances,
             StandaloneDeriving #-}

data SomeType1 = SomeType1 deriving Show
data SomeType2 = SomeType2 deriving Show

class SomeClass d where

instance SomeClass SomeType1 where
instance SomeClass SomeType2 where

data WrapperType t where
  WrapperType :: (SomeClass t, Show t) => t -> (WrapperType t)

instance Show (WrapperType SomeType1) where
  show (WrapperType d) = "correct"

instance Show (WrapperType t) where
  show (WrapperType d) = "incorrect"

data ListWrap where
  ListWrap :: [(WrapperType d)] -> ListWrap

deriving instance Show ListWrap
Run Code Online (Sandbox Code Playgroud)

现在,写作[WrapperType SomeType1]给了我想要的东西:

*MyModule> [WrapperType SomeType1]
[correct]
Run Code Online (Sandbox Code Playgroud)

但是一旦我把它放进去,ListWrap我就会选择错误的Show实例来显示内容:

*MyModule> ListWrap [WrapperType SomeType1]
ListWrap [incorrect]
Run Code Online (Sandbox Code Playgroud)

必须有类型类和/或GADT的东西我无法理解 - 它可能是什么?

J. *_*son 5

如果您查看派生代码(with -ddump-deriv),那么您可以看到派生实例ListWrap非常正常

instance Show ListWrap where
  showsPrec a (ListWrap b) =
    showParen (a >= 11) $ showString "ListWrap " . showsPrec 11 b
Run Code Online (Sandbox Code Playgroud)

它只是将内部表演showsPrec再次传递给他们.这表明问题是d当你使用ListWrap包装它时GHC正在擦除类型变量.事实上,你也可以写ListWrap

data ListWrap where
  ListWrap :: forall d. [(WrapperType d)] -> ListWrap
Run Code Online (Sandbox Code Playgroud)

强调这是存在型的.

我们可以通过删除instance Show (WrapperType t)和观察GHC的类型错误来更直接地看到错误

/Users/tel/tmp/foo.hs:33:52: Warning:
    No instance for (Show (WrapperType d))
      arising from a use of `showsPrec'
    Possible fix:
      add an instance declaration for (Show (WrapperType d))
    In the second argument of `(.)', namely `showsPrec 11 b'
    In the second argument of `($)', namely
      `showString "ListWrap " . showsPrec 11 b'
    In the expression:
      showParen (a >= 11) $ showString "ListWrap " . showsPrec 11 b
Ok, modules loaded: Main.
Run Code Online (Sandbox Code Playgroud)

换句话说,它已经失去了关于d类型的细节,因此无法统一具体instance Show (WrapperType SomeType1).


现在你会认为这意味着保持这种类型信息会使类型错误消失.

data ListWrap d where
  ListWrap :: [(WrapperType d)] -> ListWrap d

> show $ ListWrap [WrapperType SomeType1]
"ListWrap [incorrect]"
Run Code Online (Sandbox Code Playgroud)

但似乎实例搜索也出错了.我能找到使其工作的唯一方法是打开UndecidableInstances并提供实例推导的建议.

deriving instance Show (WrapperType d) => Show (ListWrap d)
Run Code Online (Sandbox Code Playgroud)

之后该示例有效

> show $ ListWrap [WrapperType SomeType1]
"ListWrap [correct]"
Run Code Online (Sandbox Code Playgroud)