可以为元组设置一个特殊的Show实例吗?

时间:2013-11-05 19:51:30

标签: haskell

我正在使用所谓的标记,这些标记是带有字符串和标记的元组,我希望我可以按以下格式在屏幕上呈现:[TAG: VALUE]我不能这样做,因为我没有做正确的事情。这是设置:

type Token value tag = ([value], tag)
data Tag = Whitespace | Alpha | Digit | Punctuation | Terminal
instance Show Tag where
    show Alpha = "A"
    show Whitespace = "W"
    show Digit = "D"
    show Punctuation = "P"
    show Terminal = "|"
type TextToken = Token Char Tag    
instance Show TextToken where
    show (values, tag) = "[" ++ show tag ++ ": " ++ values ++ "]"

编译时崩溃:

Illegal instance declaration for `Show TextToken'
  (All instance types must be of the form (T t1 ... tn)
   where T is not a synonym.
   Use -XTypeSynonymInstances if you want to disable this.)
In the instance declaration for `Show TextToken'

然后我尝试用以下内容替换实例:

instance Show ([Char], Tag) where
   show (values, tag) = "[" ++ show tag ++ ": " ++ values ++ "]"

再次遇到同样的问题:

Illegal instance declaration for `Show ([Char], Tag)'
  (All instance types must be of the form (T a1 ... an)
   where a1 ... an are *distinct type variables*,
   and each type variable appears at most once in the instance head.
   Use -XFlexibleInstances if you want to disable this.)
In the instance declaration for `Show ([Char], Tag)'

有没有办法让它发挥作用?

2 个答案:

答案 0 :(得分:10)

您需要使用newtype

newtype Tag a b = Tag (a, b)

instance (Show a, Show b) => Show (Tag a b) where
  show (Tag (a, b)) = "[" ++ show a ++ ": " ++ show b ++ "]"

你一次遇到几个实例分辨率的狡辩。

  1. 如果没有{-# LANGUAGE TypeSynonymInstances #-} pragma,您就无法在实例定义中使用type个同义词......即使它们非常清晰。启用它很好,它不是Haskell 98。

  2. 在实例定义中使用复杂,嵌套或多参数类型时,经常会遇到过于严格的Haskell 98实例定义。在许多情况下这很好,因此启用{-# LANGUAGE FlexibleInstances #-}编译指示将允许这些OK机会。

  3. 最后,危险的是,已经 Show的{​​{1}}个实例,多态的([Char], Tag) instance Show (a, b)和{{} 1}}。这意味着您将遇到a ~ [Char]警告。

  4. 您可以通过告诉GHC允许b ~ Tag使用另一个编译指示OverlappingInstances来禁用此功能,但由于它可能会为您自己和使用您的代码的其他人造成非常奇怪的运行时行为,因此非常不鼓励使用它。

    通常,如果您尝试将实例声明“特化”为特定类型,则需要一般情况不存在。

    OverlappingInstances

答案 1 :(得分:3)

你真正需要决定的是,如果你想要不同种类的Token的不同实例。如果这样做,那么使用newtype(或编译器选项,如某些建议)。如果不这样做,请将Token设为data并在通用Token类型上定义您的实例。

制作newtype包装器:

newtype TextToken = TextToken (Token Char Tag)

然后声明包装器的Show实例:

instance Show TextToken where

在你的情况下,我仍然建议你改变

type Token value tag = ([value], tag)

data Token value tag = Token [value] tag

因为你有一个记录类型,所以你也可以明确它。