【问题标题】:Deriving Show Instance for ADT not working with Higher Kinded Type Families为 ADT 派生显示实例不适用于更高种类的类型族
【发布时间】:2020-04-01 02:27:07
【问题描述】:

我刚刚使用默认示例 gist available here 处理 Chris Done 的 ADT 并遇到了一个问题:我的 ADT 具有由更高种类的类型系列定义的字段,无法使用派生的显示实例。 GHC 告诉我我需要为 Type Family 派生一个 Show 实例,但我不知道该怎么做。到目前为止,这是我所拥有的,任何 cmets 都会有所帮助。

在以下示例中(使用 ghc 8.8.1),目标是为ShowMe 定义Show 的实例,如果可能,使用派生。

{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE PartialTypeSignatures #-}
{-# LANGUAGE ConstraintKinds #-}

data Tag = A | B deriving (Show)

type family TF (p :: Tag) a where
  TF 'A a = ()
  TF 'B a = a

data ShowMe p = ShowMe
  { a :: !(TF p String)
  , b :: String
  }

main =  connect showMeDefaults { a = "some string" }
   where
     connect :: ShowMe B -> IO ()
     connect _ = pure ()
     showMeDefaults :: ShowMe A
     showMeDefaults = ShowMe { a = (), b = "asdf" }

-- This works to define Show
{-
instance Show (ShowMe p) where
  show _ = "hello"
-}
-- This instance is the line that causes an error
deriving instance Show (ShowMe p)

随后,我从 GHC 收到一个我不熟悉的错误:

show_tf.hs:35:1: error:
    • No instance for (Show (TF p String))
        arising from a use of ‘showsPrec’
    • In the first argument of ‘(.)’, namely ‘(showsPrec 0 b1)’
      In the second argument of ‘(.)’, namely
        ‘((.)
            (showsPrec 0 b1)
            ((.)
               GHC.Show.showCommaSpace
               ((.)
                  (showString "b = ") ((.) (showsPrec 0 b2) (showString "}")))))’
      In the second argument of ‘(.)’, namely
        ‘((.)
            (showString "a = ")
            ((.)
               (showsPrec 0 b1)
               ((.)
                  GHC.Show.showCommaSpace
                  ((.)
                     (showString "b = ") ((.) (showsPrec 0 b2) (showString "}"))))))’
      When typechecking the code for ‘showsPrec’
        in a derived instance for ‘Show (ShowMe p)’:
        To see the code I am typechecking, use -ddump-deriv
   |
35 | deriving instance Show (ShowMe p)

如果我们重新编译,使用ghc -ddump-deriv,将返回以下内容:

[1 of 1] Compiling Main             ( show_tf.hs, show_tf.o )

==================== Derived instances ====================
Derived class instances:
  instance GHC.Show.Show Main.Tag where
    GHC.Show.showsPrec _ Main.A = GHC.Show.showString "A"
    GHC.Show.showsPrec _ Main.B = GHC.Show.showString "B"


Derived type family instances:



==================== Filling in method body ====================
GHC.Show.Show [Main.Tag]
  GHC.Show.show = GHC.Show.$dmshow @(Main.Tag)



==================== Filling in method body ====================
GHC.Show.Show [Main.Tag]
  GHC.Show.showList = GHC.Show.$dmshowList @(Main.Tag)


Linking show_tf ...

从概念上讲,我认为我应该能够为TF 派生一个 Show 实例,但是当我这样做时,我得到以下信息:

show_tf.hs:36:31: error:
    • Illegal type synonym family application ‘TF 'A a’ in instance:
        Show (TF 'A a)
    • In the stand-alone deriving instance for
        ‘(Show a) => Show (TF 'A a)’

   |
36 | deriving instance (Show a) => Show (TF 'A a)

如果我只是尝试自己为TF 'A a 定义 Show 实例,也会出现此错误。我搜索了“非法类型同义词”,但没有想出解决方法。

【问题讨论】:

    标签: haskell types


    【解决方案1】:

    你需要添加

    {-# LANGUAGE FlexibleContexts #-}
    {-# LANGUAGE UndecidableInstances #-}
    

    然后向 GHC 建议所需的上下文:

    deriving instance Show (TF p String) => Show (ShowMe p)
    

    GHC 不会自动添加该上下文,因为它可能会让程序员感到惊讶。

    【讨论】:

    • 为什么不能为Show 编写实例TF,而且,为什么它们是不必要的?
    • @HTNW 实例必须与类型构造函数关联以允许实例解析。 TF 是一个类型族,是不允许的。事实上,我们不能真的指望 GHC 通过观察 T = F (some complex type) 来解决像 C T 这样的基本约束,然后意识到它可以使用类型族 F 的实例——这需要太神奇来解决无法确定的问题。在这种情况下,它们不是必需的,因为在 TF p String 中,p'A'B,所以 TF p String()String。 (我们可能需要一个单例来证明 p 是这样的。)
    • 谢谢!我知道TF p String 在类型族应用程序之后表示为a(),但是添加约束Show (TF p String) 背后的原因是什么?在检查ShowMe 是否是Show 的有效实例时,是否还没有应用类型族?
    • @wespiserA GHC 没有那么强大,无法进行推理步骤“TF p String 是这个或那个,在这两种情况下我们都有Show,所以我们可以假设Show”。阻止这种情况发生的原因之一是在运行时 p 是未知的(为了提高效率而删除了类型),因此无法对其进行测试,因此 GHC 在编译期间必须知道要使用哪个 Show 实例。传递上下文告诉 GHC 使用该实例。
    • @wespiserA 这与我们无法定义whichOne :: forall p . Either (p :~: 'A) (p :~: 'B) 的事实有关,这将证明pAB。我们可以在像 Agda 或 Coq 这样的依赖类型语言中做到这一点,但在 Haskell 中,由于类型擦除,我们不能这样做。更糟糕的是,Haskell 允许 type family G t :: Tag 没有任何定义方程,导致 G Bool :: Tag 即使不能简化为 AB - 一种“未指定的标签常量”。这当然使处理TF (G Bool) String 成为不可能,因为我们无法简化该类型。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-04
    • 2013-08-31
    • 2014-10-02
    • 1970-01-01
    • 2022-09-30
    • 1970-01-01
    相关资源
    最近更新 更多