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

Rah*_*nne 22 haskell ml sml

根据Harper(https://existentialtype.wordpress.com/2011/04/16/modules-matter-most/),似乎Type Classes根本没有提供与Modules提供的相同级别的抽象,而我正在拥有很难确切地弄清楚原因.并且在该链接中没有示例,因此我很难看到关键差异.还有其他关于如何在模块和类型类之间进行翻译的论文(http://www.cse.unsw.edu.au/~chak/papers/modules-classes.pdf),但这对于从程序员的角度来看实现(它只是说没有人可以做的事情,另一个人无法模仿).

具体来说,在第一个链接中:

首先,他们坚持认为类型可以完全以一种方式实现类型类.例如,根据类型类的哲学,整数可以以一种方式(通常的顺序)排序,但显然有许多有趣的排序(例如,通过可分性).第二个问题是它们混淆了两个不同的问题:指定类型如何实现类型类,以及指定在类型推断期间何时应该使用这样的规范.

我也不明白.一个类型可以在ML中以多种方式实现类型类?如果不创建新类型,您将如何通过示例按可分性排序整数?在Haskell中,您必须执行诸如使用数据之类的操作,并instance Ord提供替代排序.

第二个,不是两个在Haskell中是截然不同的吗?指定"何时应在类型推断期间使用此类规范"可以通过以下方式完成:

blah :: BlahType b => ...
Run Code Online (Sandbox Code Playgroud)

其中BlahType是在类型推断期间使用的类而不是实现类.然而,"类型如何实现类型类"是使用完成的instance.

有人可以解释链接真正想说的内容吗?我只是不太明白为什么模块比Type Classes更少限制.

Kwa*_*rtz 19

要理解文章的内容,请花点时间考虑一下MonoidHaskell中的类型类.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
Run Code Online (Sandbox Code Playgroud)

有许多Haskell类型符合这个定义.我们可以直接想到的一个例子是整数,我们可以为此定义以下内容.

instance Monoid Integer where
    mappend = (+)
    mempty = 0
Run Code Online (Sandbox Code Playgroud)

您可以确认所有要求都已成立.

a + (b + c) == (a + b) + c
a + 0 == 0 + a == a
Run Code Online (Sandbox Code Playgroud)

实际上,这些条件适用于所有数字而非添加,因此我们也可以定义以下内容.

instance Num a => Monoid a where
    mappend = (+)
    mempty = 0
Run Code Online (Sandbox Code Playgroud)

所以现在,在GHCi中,我们可以做到以下几点.

> mappend 3 5
8
> mempty
0
Run Code Online (Sandbox Code Playgroud)

特别敏锐的读者(或具有数学背景的读者)现在可能已经注意到我们也可以定义Monoid数字乘法的实例.

instance Num a => Monoid a where
    mappend = (*)
    mempty = 1

a * (b * c) == (a * b) * c
a * 1 == 1 * a == a
Run Code Online (Sandbox Code Playgroud)

但是现在编译器遇到了问题.它mappend应该用于数字的定义?是mappend 3 5等于8还是15?它没有办法决定.这就是Haskell不允许单个类型类的多个实例的原因.但问题仍然存在.我们应该使用哪个Monoid实例Num?两者都是完全有效的,并且对某些情况有意义.解决方案是既不使用也不使用.如果你看一下Monoid在Hackage,你会看到,有没有Monoid的情况下Num,或Integer,Int,Float,或Double为这一问题.相反,还有Monoid的实例SumProduct.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
Run Code Online (Sandbox Code Playgroud)

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

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

  • 目前,在Haskell扩展(OverlappingInstances)中存在一个错误,它偶然允许在某些情况下指定一个类型的更多实例,以及在一个案例中选择哪一个.但这被认为是一个错误.相反,在Agda中,类字典是隐式参数,如果需要,可以在每次调用时指定.因此,在Agda中,我们可以为同一类型设置多个实例(代价是必须选择在每次调用时使用哪个实例).http://stackoverflow.com/questions/29504107/which-dictionary-does-ghc-choose-when-more-than-one-is-in-scope (5认同)
  • 虽然对于"Monoid"类可能是这样,但是很容易创建一个多参数类型类,它具有一个"tag"参数,用于指定所需的实例.通常,您必须指定它,但这类似于使用限定模块名称.在参考文献中,编码使用关联的类型系列而不是多参数类型类,但想法是相同的. (3认同)
  • ML如何解决这种模糊性?是的,在Haskell中,你最终必须将它包装成另一种类型.但ML如何处理呢?"A型可以实现在ML超过1种方式类型的类?你会如何有由例如整除订购的整数,而无需创建一个新的类型?" 这并没有真正回答. (2认同)