类声明和实例声明之间有什么区别?

3
我目前正在阅读这个,但说实话,我很难理解它的内容。
class Eq a where 
  (==)                  :: a -> a -> Bool

实现,其中
instance Eq Integer where 
  x == y                =  x `integerEq` y

第一个代码段并没有实现什么功能。我理解第二个代码段定义了对两个整型变量执行等值比较的结果应该是什么。那么第一个代码段的目的是什么呢?


8
一个是类型类,另一个是类型实例。前者描述这个类型类的类型可以做什么(接口),后者描述如何完成它(实现)。(免责声明:我真的不懂 Haskell。这只是从语法和我所读的很少的内容猜测出来的。) - Xeo
1
@Xeo 你说得对。有人可能会认为 Haskell 的类型类与 Java 的接口相匹配。Haskell 的实例则与 Java 的接口实现相匹配。 - Simon Bergot
3
Haskell的类型类与面向对象编程无关。 - Matvey Aksenov
4个回答

9
声明表示“我现在要定义一堆函数,它们可以适用于几种不同的数据类型”。声明则表示“这就是这些函数在数据类型上的运作方式”。
在您的具体示例中,class Eq表示“Eq指任何具有名为==的函数的数据类型”,而instance Eq Integer表示“这就是==Integer上的运作方式”。

4

首先定义了一个类型必须提供哪些操作才能比较相等。你可以使用这个定义来编写函数,该函数可以操作任何可比较相等的类型,而不仅仅是整数。

allSame :: Eq a => [a] -> Bool
allSame [] = []
allSame (x:xs) = foldr True (==x) xs

这个函数适用于整数,因为存在 Eq Integer 的实例。它也适用于字符串 ([Char]),因为存在 Eq Char 的实例,并且对于具有 Eq 实例的类型的列表也存在实例 (instance Eq a => Eq [a])。


第一个是像面向对象接口吗?这两个有关系吗?可以使用没有实例的类,反之亦然吗? - intrigued_66
有一些相似之处,但它们并不完全相同。您可以编写使用类的函数而不需要定义任何实例。但是,如果没有该类的实例类型,您无法调用这些函数。我帖子中的示例不需要存在任何实例,但需要调用该函数。无论如何,我认为以面向对象的方式思考它并不好。 - R. Martinho Fernandes
不,Eq a 的意思是“无论谁调用这个函数,只要该类型 a 实现了 Eq 类型类,就可以使用任何类型 a。” 任何尝试使用未实现该类型类的类型调用该函数都将导致编译时错误。 - dave4420
5
虽然它们并不完全相关,但就类型类的基本错误比喻而言,“像面向对象接口”是我所知道的最不错误的。总的来说,类型类比接口更加强大,但在许多方面它们的作用类似。 - C. A. McCann
我觉得有必要提一下,即使是我上面对类型类的描述也仅限于特定子集的类型类。这就是类型类/接口比较的另一个问题:它并不能真正帮助理解类型类的许多更有趣的用法。 - John L
显示剩余2条评论

2

对于不同类型,有一个类和许多实例。这就是为什么类指定所需的签名(接口;类还可以指定默认实现,但这与重点无关),而实例则指定主体(实现)。然后,您可以使用类名作为约束,表示“任何实现 Eq 操作的类型 a ,即在 Eq 中具有实例的类型”。

阅读Learn you a HaskellReal World Haskell,它们比haskell.org教程更好。


你同意Xeo的评论吗?如果是这样,我理解了。 - intrigued_66

0
假设您想要实现一个通用算法或数据结构,"通用"意味着多态:它应该适用于任何数据类型。例如,假设您想编写一个函数来确定三个输入值是否相等。
针对特定的(单态)情况,您可以为整数执行此操作:
eq3 :: Int -> Int -> Int -> Bool
eq3 x y z = x == y && y == z

我们当然希望上述定义也适用于其他类型,但如果我们只是告诉编译器该函数应该适用于任何类型:
eq3 :: a -> a -> a -> Bool
eq3 x y z = x == y && y == z

...编译器抱怨我们的通用 a 不适用于 == 函数:

<interactive>:12:49:
    No instance for (Eq a)
      arising from a use of `=='
    In the first argument of `(&&)', namely `x == y'
    In the expression: x == y && y == z
    In an equation for `eq3': eq3 x y z = x == y && y == z

我们必须告诉编译器,我们的类型 aEq 类型类的一个实例,你已经注意到这是 == 函数声明的地方。看看这里的区别:
eq3 :: Eq a => a -> a -> a -> Bool
eq3 x y z = x == y && y == z

现在我们有一个函数,可以统一地操作任何属于Eq类型类的类型a

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接