根据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
要理解文章的内容,请花点时间考虑一下Monoid
Haskell中的类型类.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
的实例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
Run Code Online (Sandbox Code Playgroud)
现在,如果你想使用一个数字,Monoid
你必须将它包装在一个Sum
或Product
类型中.您使用哪种类型决定使用哪个Monoid
实例.这是本文试图描述的内容的本质.Haskell的类型类系统中没有内置系统,允许您在多个内容之间进行选择.相反,你必须通过在骨架类型中包装和展开它们来跳过箍.现在,无论你是否认为这是一个问题,很大程度上决定了你是喜欢Haskell还是ML.
ML通过允许在不同模块中定义相同类和类型的多个"实例"来解决这个问题.然后,导入的模块将确定您使用的"实例".(严格来说,ML没有类和实例,但它确实有签名和结构,它们的行为几乎相同.为了深入比较,请阅读本文).