为什么在Haskell中显式推导Show / Read?

时间:2012-07-04 17:00:07

标签: haskell derived-types

我们无法为每种类型read someValue :: someDataTypeshow someValue执行,因为必须在deriving (Show, Read)声明中写入data。是否有一个案例,除了错误,我们不希望我们的类型可序列化? 为什么Show与Read分开?是否有一个案例,除了错误,我们只想显示一些数据而不读它?如果没有,为什么不使用单一数据类型Serializable

刚才,我正在使用the Key datatype of the Gloss library导出Show而不是Read,这是我不明白的。这很遗憾,因为我想将控件的配置放在一个文件中,然后读取它,这样玩家就可以更改控件并拥有自己的配置。 我不得不为Key,SpecialKey和MouseButton做包装,这不是什么大问题但是没用。

data Key' = Char' Char | SpecialKey' SpecialKey | MouseButton' MouseButton
    deriving (Eq, Ord, Show, Read)
convertKey x = case x of
    Char' c -> Char c
    SpecialKey' sk -> SpecialKey sk
    MouseButton' mb -> MouseButton mb

3 个答案:

答案 0 :(得分:11)

首先,并非所有数据类型都可以显示,例如无法显示函数,因此不能显示(也不能读取)所有数据类型。最初的Haskell定义指定如果没有给出派生子句,那么将导出尽可能多的派生类。这使得很难知道实际导出了哪些类,因此更改了Haskell定义以强制显式派生子句。

其次,在原始的Haskell定义中,showread函数在Text类中捆绑在一起。当你导出ShowRead时,这不是什么大问题,但是当你手工编写它们时会很痛苦。您经常要定义一个特殊的show函数,但现在您也被迫编写read函数,因此最好将它们分开。就个人而言,我几乎总是派生Show,但几乎从不Read

ShowRead类并不是真正用于序列化,而是用于简单的输入和输出。

答案 1 :(得分:10)

为什么Show与Read

分开

我不知道为什么这是最初的,但是觉得它应该持久,因为可以显示一些(非常少)类型,或者显示占位符字符串,但是不能读回来。函数是典型的例子。

考虑它的另一种方式:将事物放在单独的类中非常容易,但很难在一个类中处理太多的函数,这些函数在相同的上下文中并不总是有意义的。许多人认为Num类是这个问题的一个主要例子。

我如何read关键字和其他类型不在Read

第一步:发送一个补丁,将Read添加到派生实例集。第二步:使用独立派生来解决问题:

{-# LANGUAGE StandaloneDeriving #-}
deriving instance Show Key

第三步:使用CPP使您的代码可以使用任何版本的代码库,无论是具有Ben有朝一日会发布的Read实例的光泽库,还是没有版本的。

为什么没有Serializable类?

对于初学者来说,有a Serialize class。此外,文本是序列化事物的可怕方式。也许你想要一个更加懒惰的序列化类,在这种情况下你应该看到Binary class。如果你担心表现,那么你可能会喜欢blaze-builder,虽然我老实说从未使用它。

答案 2 :(得分:7)

并非所有类型都可序列化。如何在String -> StringString之间建立同构?如果您为Read提供ShowString -> String个实例,我可以找到一个不可序列化的函数:

evil :: String -> String
evil s = map succ (read s s ++ " evil")

假设

read (show evil) = evil

我们得到了

evil (show evil)
  = map succ (read (show evil) (show evil) ++ " evil")
  = map succ (evil (show evil) ++ " evil")
  = map succ (evil (show evil)) ++ "!fwjm"

所以如果定义了evil (show evil),那么第一个字符c就会满足c = succ c,这是不可能的。

通常,函数无法序列化。有时,我们编写打包函数的数据类型,因此并非每个数据类型都可序列化。如,

data Psychiatrist
  = Listen (String -> Psychiatrist)
  | Charge Int

有时,即使对于这些类型,您也可以选择提供Read(有些情况下缺失)和Show的部分实现(例如,使用占位符或函数列表),但是没有规范的方式来选择它们或理由为什么你会期望两者。

正如其他人所提到的,严重的序列化是Serialize的保留。我倾向于使用ShowRead进行诊断,特别是在ghci中尝试。为此,Show更有用,因为ghci有一个 Haskell 解析器来进行读取。