【问题标题】:What does deriving do/mean in Haskell?在 Haskell 中推导是什么/意味着什么?
【发布时间】:2017-06-25 09:12:04
【问题描述】:

栈溢出推导的定义为:

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

说实话,我一点都不懂。

以下代码摘自:Link

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

barryBonds :: BaseballPlayer -> Bool
barryBonds Outfielder = True

barryInOf = print(barryBonds Outfielder)

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

【问题讨论】:

  • 不,不是重复的。
  • 很难回答您的问题,因为您没有设置上下文。您唯一的陈述是“我不知道(这个或那个)”。你不知道的事情需要用你知道的事情来解释,没有人知道这些是什么。我们应该从类型开始吗?职能?类和实例?没有线索。事实上,这个问题过于宽泛。
  • 您实际上可以通过将标志 -ddump-deriv 传递给 GHC 来确切地看到它的作用。这将打印所有deriving 子句生成的代码。这就是它所做的一切——为你生成一些样板文件。

标签: haskell functional-programming deriving


【解决方案1】:

简而言之:

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

为了使ComparableColor 一起工作,我们实现了instanceComparable

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

粗略地说,现在您可以“比较”RedGreenBlue。但是 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)

然而,关键是:对于任意用户定义的类型类,当您尝试将类型类与ColorBaseballPlayer 等数据类型相关联时,GHC 不知道应该如何实现它们的功能。对于一些类型类,如ShowEqOrd等,功能足够简单,GHC可以生成默认实现,当然你可以自己重写。

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

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 的功能,但那是另一天的事情了 :)

【讨论】:

    【解决方案2】:

    在这种特定情况下,它会为您的类型生成一个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 类成员完成的,但这并不重要。

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

        猜你喜欢
        • 2020-01-21
        • 2023-03-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-12-05
        • 1970-01-01
        相关资源
        最近更新 更多