6

堆栈溢出推导的定义是:

“在 Haskell 中,派生实例是与数据或新类型声明一起自动生成的实例声明。派生实例声明的主体在语法上是从关联类型的定义中派生的。”

老实说,我真的不明白其中的任何一个。

下面的代码取自:链接

data BaseballPlayer = Pitcher 
                        | Catcher
                        | Infielder
                        | Outfielder
                        deriving Show

barryBonds :: BaseballPlayer -> Bool
barryBonds Outfielder = True

barryInOf = print(barryBonds Outfielder)

我的问题是,派生语句在这种特定情况下做什么,以及派生语句一般做什么?

4

3 回答 3

13

简而言之:

deriving自动为一些 Haskell 的类型类实现函数,例如ShowEq。这不能用任意类型类来完成,但那些deriving确实适用的类型类足够简单,可以自动实现。

Show类型类定义了如何将数据类型表示为String.

更广泛:

你熟悉类型类吗?

https://www.haskell.org/tutorial/classes.html

类型类类似于 Java 中的接口:它们定义了一些函数,任何想要使用这些函数的数据类型都可以实现这些函数。

例如,假设我们有一个这样的类:

class Comparable a where
    lessThan :: a -> a -> Bool
    equalsTo :: a -> a -> Bool

当心这个词class。它意味着 Haskell 设置中的类型类,而不是您在面向对象语言中听到的典型“类”。这里a是一种填充类型,类似于您期望模板在 C++ 中的工作方式和泛型在 Java 中的行为方式。

假设我们定义一个数据类型如下:

data Color = Red | Green | Blue

为了Comparable与 一起工作Color,我们实现了instanceof Comparable

instance Comparable Color where
    lessThan Red   Green = True
    lessThan Red   Blue  = True
    lessThan Green Blue  = True
    lessThan _     _     = False

    equalsTo Red   Red   = True
    equalsTo Green Green = True
    equalsTo Blue  Blue  = True
    equalsTo _     _     = False

粗略地说,这现在允许您“比较” Red, Green, 和Blue彼此。但是有什么方法可以让 GHC 自动猜到这就是您想要的确切“顺序”?

退后一步,类型类Show具有类似的结构:

https://hackage.haskell.org/package/base-4.9.1.0/docs/src/GHC.Show.html#Show

class  Show a  where
    showsPrec :: Int -> a -> ShowS
    show      :: a   -> String
    showList  :: [a] -> ShowS

    showsPrec _ x s = show x ++ s
    show x          = shows x ""
    showList ls   s = showList__ shows ls s

需要注意的是,类型类中的函数可以相互定义。事实上,我们也可以轻松做到:

class Comparable a where
    lessThan :: a -> a -> Bool
    equalsTo :: a -> a -> Bool

    greaterThan :: a -> a -> Bool
    greaterThan lhs rhs = not (lessThan lhs rhs && equalsTo lhs rhs)

Color但是,关键点是:对于任意用户定义的类型类,当您尝试将类型类与诸如or之类的数据类型相关联时,GHC 不知道应该如何实现它们的功能BaseballPlayer。对于一些功能足够简单的类型类,例如Show, Eq,等,GHC 可以生成默认实现,您当然可以自己覆盖这些实现。Ord

事实上,让我们尝试编译以下内容来进行实验:

data Color = Red | Green | Blue deriving (Comparable)

我得到的结果是这样的:

test.hs:9:43:
    Can't make a derived instance of ‘Comparable Color’:
      ‘Comparable’ is not a derivable class
      Try enabling DeriveAnyClass
    In the data declaration for ‘Color’

当然,可以使用一些 GHC 扩展来扩展 的功能deriving,但那是另一天 :)

于 2017-06-25T11:58:20.157 回答
6

在这种特定情况下,它会Show为您的类型生成一个实例,如下所示:

instance Show BaseballPlayer where
    show Pitcher    = "Pitcher"
    show Catcher    = "Catcher"
    show Infielder  = "Infielder"
    show Outfielder = "Outfielder"

通过这种方式,类型的值BaseballPlayer可以转换为字符串,例如通过print.

选择字符串是为了使其成为有效的 Haskell 表达式,一旦评估,就可以重建原始值。

一般情况稍微复杂一些,但遵循相同的想法:将值转换为 Haskell 表达式字符串。例如

data T = T Int (Maybe Bool) deriving Show

将使实例如此

show (T 1 Nothing) = "T 1 Nothing"
show (T 2 (Just 3)) = "T 2 (Just 3)"

请注意,在最后一种情况下,括号也是如何生成的。这是使用showsPrec类成员完成的,但这并不重要。

于 2017-06-25T09:27:00.583 回答
3

派生意味着您的数据类型能够自动“派生”某些类型类的实例。在这种情况下BaseballPlayerShow这意味着我们可以使用任何需要 的实例才能使用的Show函数BaseballPlayer

自动派生使您更容易避免样板。用于自动派生的最常见类型类是ShowEq,因为编译器可以为这些类型类生成非常合理的值。

于 2017-06-25T09:26:14.213 回答