访问 Haskell 中的 "default show"?

Accessing the "default show" in Haskell?

假设你有一个数据结构(从这个 question 借来的):

data Greek = Alpha | Beta | Gamma | Delta | Eta | Number Int

现在可以通过在该指令上附加 deriving Show 使其成为 Show 的实例。

尽管我们希望将 Number Int 显示为:

instance Show Greek where
    show (Number x) = show x
    -- ...

问题是必须指定 Greek 数据的所有其他部分以及:

    show Alpha = "Alpha"
    show Beta = "Beta"

对于这个当然可行的小例子。但是如果选项的个数比较长,工作量就大了。

我想知道是否可以访问 "default show" 实现并使用通配符调用它。例如:

instance Show Greek where
    show (Number x) = show x
    show x = defaultShow x

您因此 "implement" 与默认方法不同的特定模式和其余模式由 "fallback mechanism" 解决。

有点类似于面向对象编程中对 super.method 的方法覆盖。

不,据我所知这不可能。

此外,Show 的自定义实例值得重新考虑,因为 ShowRead 实例应该相互兼容。

如果只是转换为人类(或任何人)可读的字符串,请使用您自己的函数或您自己的类型类。这也将实现你想要的:

假设你有一个 Presentable 带有方法 present 的类型类,还有默认的 Show 实例,你可以这样写:

instance Presentable Greek where
    present (Number x) = show x
    present x = show x

您可以使用 Data 和 Typeable 完成此操作。这当然是一个黑客,这个例子只适用于 "enumerated" 类型,就像你的例子一样。

我相信我们可以更详细地说明我们是如何做到这一点的,但为了涵盖您给出的示例:

{-# LANGUAGE DeriveDataTypeable #-}
import Data.Data
import Data.Typeable

data Greek = Alpha | Beta | Gamma | Delta | Eta | Number Int 
             deriving (Data,Typeable)

instance Show Greek where
    show Number n = show n
    show x = show $ toConstr x

我实现的这种方法不能处理嵌套数据结构或任何其他远程奇特的东西,但同样,这是一个丑陋的 hack。如果你真的 必须 使用这种方法,你可以在 Data.Data 包中挖掘我相信你可以拼凑一些东西......

这里有一篇博客 post 对软件包进行了快速介绍:http://chrisdone.com/posts/data-typeable

解决此问题的正确方法是使用 newtype 包装器。我意识到这不是最方便的解决方案,尤其是在使用 GHCi 时,但它不会产生额外的开销,并且随着程序的增长不太可能以意想不到的方式中断。

data Greek = Alpha | Beta | Gamma | Delta | Eta | Number Int 
         deriving (Show)

newtype SpecialPrint = SpecialPrint Greek

instance Show SpecialPrint where
    show (SpecialPrint (Number x)) = "Number: " ++ show x
    show (SpecialPrint x) = show x

main = do
    print (SpecialPrint Alpha)
    print (SpecialPrint $ Number 1)

正如@phg 在评论中指出的那样,这也可以在 generic-deriving:

的帮助下完成
{-# LANGUAGE DeriveGeneric #-}
module Main where

import           Generics.Deriving.Base (Generic)
import           Generics.Deriving.Show (GShow, gshow)

data Greek = Alpha | Beta | Gamma | Delta | Eta | Number Int
  deriving (Generic)

instance GShow Greek
instance Show Greek where
  show (Number n) = "n:" ++ show n
  show l = gshow l

main :: IO ()
main = do
  print (Number 8)
  print Alpha