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

L01*_*man 3 haskell derived-types

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

刚才,我正在使用Gloss库Key数据类型,它是派生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
Run Code Online (Sandbox Code Playgroud)

aug*_*tss 11

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

其次,在原始的Haskell定义中show,read函数在Text类中捆绑在一起.当你得到这不是太大的问题ShowRead,但它是一种痛苦,当你手工编写它们.您经常要定义一个特殊的show函数,但现在您也被迫编写一个read函数,因此最好将它们分开.就个人而言,我几乎总是来自Show,但几乎从不Read.

ShowRead班是不是真的意味着要用于序列化,而是进行简单的输入和输出.


Tho*_*son 10

为什么Show与Read分开

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

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

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

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

{-# LANGUAGE StandaloneDeriving #-}
deriving instance Show Key
Run Code Online (Sandbox Code Playgroud)

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

为什么不Serializable上课?

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


pig*_*ker 7

并非每种类型都可序列化.你如何在String -> String和之间建立同构String?如果你给我ReadShow实例String -> String,我可以找到一个不可序列化的函数:

evil :: String -> String
evil s = map succ (read s s ++ " evil")
Run Code Online (Sandbox Code Playgroud)

假设

read (show evil) = evil
Run Code Online (Sandbox Code Playgroud)

我们得到了

evil (show evil)
  = map succ (read (show evil) (show evil) ++ " evil")
  = map succ (evil (show evil) ++ " evil")
  = map succ (evil (show evil)) ++ "!fwjm"
Run Code Online (Sandbox Code Playgroud)

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

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

data Psychiatrist
  = Listen (String -> Psychiatrist)
  | Charge Int
Run Code Online (Sandbox Code Playgroud)

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

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