为什么将衍生阅读,显示在下面以及它们做什么是一个好主意?
答案 0 :(得分:5)
当你定义自己的数据类型时,通常会有许多类型的样板代码,你真的不想自己编写。编译器通常可以为您解决这个问题。
例如,如果你有类型
data Direction = North | South | East | West
你将无法做到
if direction == North then ...
因为您尚未为Eq
编写Direction
的实例。你可以写
instance Eq Direction where
North == North = True
South == South = True
East == East = True
West == West = True
_ == _ = False
但这是额外的6行代码,它们无法帮助您或其他人理解您的代码。同样适用于Show
,Ord
,Enum
,Bounded
,Read
和Functor
等类,这些都占用宝贵的时间和当编译器自己解决这个问题时,写入的能量是非常微不足道的。这就是deriving
子句的用武之地。我提到的所有类型类都是可派生的(虽然Functor
需要GHC中的DeriveFunctor
扩展名),还有一些其他类型。它所做的只是指示编译器找出这些类型类本身的实现,所以
data Direction = North | South | East | West
deriving (Eq, Ord, Enum, Bounded, Show, Read)
会生成很多你可能自己编写的代码,但这样做并不是很有趣。如果需要,您可以看到通过使用-ddump-deriv
标志进行编译而生成的代码,但由于它是编译器生成的,因此它不会非常漂亮。
答案 1 :(得分:2)
实例派生是Haskell中的一个强大功能,它使编译器自动为您创建的类型创建一些类型的合理实例。通常情况下,Eq
,Ord
,Show
,Read
,Enum
和Bounded
等内容都有合理的默认实例,编译器可以猜测。例如,有限和类型上的Eq
。
data Sum = A | B | C deriving ( Eq )
-- is equivalent to
instance Eq Sum where
A == A = True
B == B = True
C == C = True
_ == _ = False
在适当的情况下,导出可以减少很多样板。总而言之,推导被认为是Haskell的核心功能,它已被GeneralizedNewtypeDeriving
,DeriveFunctor
,DeriveFoldable
,DeriveTraversable
,DeriveGeneric
等编译器扩展扩展。和DeriveDataTypeable
并由社区使用Generics
派生和TemplateHaskell
自动生成的实例来接受。
例如,如果您使用aeson
Haskell-JSON数据绑定库,您将经常看到模板Haskell
deriveJSON ''MyType
会自动为ToJSON
和FromJSON
创建实例。
总之,这些都是确保只需要编写代码中最重要和特定部分的方法。派生实例往往是“最简单的可能实例”,因此可能具有最不令人惊讶的行为。在构成基本功能层之后,可以更直接地编码与这些默认值的小偏差。
答案 2 :(得分:1)
Read
,Show
,Eq
和朋友是类型类。基本上它们是可以创建某些功能的接口,例如show
,read
,==
以及其他适用于您的类型的功能。您提到的两个Show
和Read
在您的类型上定义了两个函数。
read :: String -> YourType
show :: YourType -> String
事实上,它实际上做的是生成一个类型类实例,让你可以使用read
和show
,就好像它们有这些类型一样。
让deriving
处理这个问题通常很好,因为它只是样板文件,通常用于调试。通常你只想打印出来的东西,deriving Show
给你一个很好的快速方法。
实质上,deriving
只是让编译器为您生成代码的一种方法。这对于Eq
,Ord
和Bounded
特别有用,您可以轻松地犯一个非常难以调试的琐碎错误。