根据Harper(https://existentialtype.wordpress.com/2011/04/16/modules-matter-most/)的说法,类型类似乎不能提供与模块提供的抽象级别相同的抽象级别,而且我很难确切地找出原因。并且在该链接中没有示例,因此我很难看到关键差异。还有其他关于如何在模块和类型类之间进行转换的论文(http://www.cse.unsw.edu.au/~chak/papers/modules-classes.pdf),但这与程序员的观点中的实现没有任何关系(它只是说没有一个可以做到对方无法效仿。)
具体来说,在first link:
首先,他们坚持认为类型可以用一种方式实现类型类。例如,根据类型类的哲学,整数可以以一种方式(通常的顺序)排序,但显然有许多有趣的排序(例如,通过可分性)。第二个问题是它们混淆了两个不同的问题:指定类型如何实现类型类,以及指定在类型推断期间何时应该使用这样的规范。
我也不明白。一个类型可以在ML中以多种方式实现类型类?如果不创建新类型,您将如何通过示例按可分性排序整数?在Haskell中,您必须执行诸如使用数据之类的操作并让instance Ord
提供替代排序。
第二个,不是两个在Haskell中是截然不同的吗? 指定“何时应在类型推断期间使用此类规范”可以通过以下方式完成:
blah :: BlahType b => ...
其中BlahType是在类型推断期间使用的类而不是实现类。然而,“类型如何实现类型类”是使用instance
完成的。
有人可以解释链接真正想说的内容吗?我只是不太明白为什么模块比Type Classes更少限制。
答案 0 :(得分:19)
要了解文章的内容,请花一点时间考虑Haskell中的Monoid
类型类。 monoid是任何类型T
,它具有函数mappend :: T -> T -> T
和标识元素empty :: T
,其中包含以下内容。
a `mappend` (b `mappend` c) == (a `mappend` b) `mappend` c
a `mappend` mempty == mempty `mappend` a == a
有许多适合此定义的Haskell类型。我们可以直接想到的一个例子是整数,我们可以为此定义以下内容。
instance Monoid Integer where
mappend = (+)
mempty = 0
您可以确认所有要求都已成立。
a + (b + c) == (a + b) + c
a + 0 == 0 + a == a
实际上,这些条件适用于所有数字而非添加,因此我们也可以定义以下内容。
instance Num a => Monoid a where
mappend = (+)
mempty = 0
现在,在GHCi中,我们可以做到以下几点。
> mappend 3 5
8
> mempty
0
特别敏锐的读者(或具有数学背景知识的读者)现在可能已经注意到我们还可以为乘法上的数字定义Monoid
个实例。
instance Num a => Monoid a where
mappend = (*)
mempty = 1
a * (b * c) == (a * b) * c
a * 1 == 1 * a == a
但是现在编译器遇到了问题。它应该用于数字的mappend
定义? mappend 3 5
等于8
还是15
?它没有办法决定。这就是Haskell不允许单个类型类的多个实例的原因。但问题仍然存在。我们应该使用哪个Monoid
Num
个实例?两者都是完全有效的,并且对某些情况有意义。解决方案是既不使用也不使用。如果您在Hackage中查看Monoid
,您会看到Monoid
或Num
,Integer
,Int
没有Float
个实例,或者Double
就此而言。相反,Monoid
和Sum
有Product
个实例。 Sum
和Product
的定义如下。
newtype Sum a = Sum { getSum :: a }
newtype Product a = Product { getProduct :: a }
instance Num a => Monoid (Sum a) where
mappend (Sum a) (Sum b) = Sum $ a + b
mempty = Sum 0
instance Num a => Monoid (Product a) where
mappend (Product a) (Product b) = Product $ a * b
mempty = Product 1
现在,如果您想将数字用作Monoid
,则必须将其包含在Sum
或Product
类型中。您使用哪种类型决定使用哪个Monoid
实例。这是本文试图描述的内容的本质。 Haskell的类型类系统中没有内置系统,允许您在多个内容之间进行选择。相反,你必须通过在骨架类型中包装和展开它们来跳过箍。现在,您是否认为这是一个问题,这在很大程度上决定了您是喜欢Haskell还是ML。
ML通过允许在不同模块中定义相同类和类型的多个“实例”来解决这个问题。然后,导入的模块将确定您使用的“实例”。 (严格来说,ML没有类和实例,但它确实有签名和结构,它们的行为几乎相同。为了深入比较,请阅读this paper)。