在Haskell中定义代数数据类型时

时间:2013-11-25 16:23:58

标签: haskell types show

为什么将衍生阅读,显示在下面以及它们做什么是一个好主意?

3 个答案:

答案 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行代码,它们无法帮助您或其他人理解您的代码。同样适用于ShowOrdEnumBoundedReadFunctor等类,这些都占用宝贵的时间和当编译器自己解决这个问题时,写入的能量是非常微不足道的。这就是deriving子句的用武之地。我提到的所有类型类都是可派生的(虽然Functor需要GHC中的DeriveFunctor扩展名),还有一些其他类型。它所做的只是指示编译器找出这些类型类本身的实现,所以

data Direction = North | South | East | West
    deriving (Eq, Ord, Enum, Bounded, Show, Read)

会生成很多你可能自己编写的代码,但这样做并不是很有趣。如果需要,您可以看到通过使用-ddump-deriv标志进行编译而生成的代码,但由于它是编译器生成的,因此它不会非常漂亮。

答案 1 :(得分:2)

实例派生是Haskell中的一个强大功能,它使编译器自动为您创建的类型创建一些类型的合理实例。通常情况下,EqOrdShowReadEnumBounded等内容都有合理的默认实例,编译器可以猜测。例如,有限和类型上的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的核心功能,它已被GeneralizedNewtypeDerivingDeriveFunctorDeriveFoldableDeriveTraversableDeriveGeneric等编译器扩展扩展。和DeriveDataTypeable并由社区使用Generics派生和TemplateHaskell自动生成的实例来接受。

例如,如果您使用aeson Haskell-JSON数据绑定库,您将经常看到模板Haskell

deriveJSON ''MyType

会自动为ToJSONFromJSON创建实例。

总之,这些都是确保只需要编写代码中最重要和特定部分的方法。派生实例往往是“最简单的可能实例”,因此可能具有最不令人惊讶的行为。在构成基本功能层之后,可以更直接地编码与这些默认值的小偏差。

答案 2 :(得分:1)

ReadShowEq和朋友是类型类。基本上它们是可以创建某些功能的接口,例如showread==以及其他适用于您的类型的功能。您提到的两个ShowRead在您的类型上定义了两个函数。

read :: String -> YourType
show :: YourType -> String

事实上,它实际上做的是生成一个类型类实例,让你可以使用readshow,就好像它们有这些类型一样。

deriving处理这个问题通常很好,因为它只是样板文件,通常用于调试。通常你只想打印出来的东西,deriving Show给你一个很好的快速方法。

实质上,deriving只是让编译器为您生成代码的一种方法。这对于EqOrdBounded特别有用,您可以轻松地犯一个非常难以调试的琐碎错误。