(ML)模块vs(Haskell)类型类

时间:2016-04-28 23:33:24

标签: haskell sml ml

根据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更少限制。

1 个答案:

答案 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,您会看到MonoidNumIntegerInt没有Float个实例,或者Double就此而言。相反,MonoidSumProduct个实例。 SumProduct的定义如下。

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,则必须将其包含在SumProduct类型中。您使用哪种类型决定使用哪个Monoid实例。这是本文试图描述的内容的本质。 Haskell的类型类系统中没有内置系统,允许您在多个内容之间进行选择。相反,你必须通过在骨架类型中包装和展开它们来跳过箍。现在,您是否认为这是一个问题,这在很大程度上决定了您是喜欢Haskell还是ML。

ML通过允许在不同模块中定义相同类和类型的多个“实例”来解决这个问题。然后,导入的模块将确定您使用的“实例”。 (严格来说,ML没有类和实例,但它确实有签名和结构,它们的行为几乎相同。为了深入比较,请阅读this paper)。