typeclasses是必不可少的?
我曾经问过一个关于haskell初学者的问题,不管是使用data / newtype还是一个typeclass。 在我的特殊情况下,事实证明不需要类型类。 此外汤姆埃利斯给了我一个明智的建议,如果有疑问应该怎么做:
回答这个问题最简单的方法是:
使用数据
我知道typeclasses可以使一些事情变得更漂亮,但AFIK并不多。 这也让我觉得typeclasses主要用于脑干的东西,wheras更新的东西,新的typeclasses几乎没有被引入,一切都是用数据/新类型完成的。
现在我想知道是否有这样的情况,绝对需要类型类,并且不能用data / newtype表示事物?
在StackOverflow上回答类似的问题Gabriel Gonzales说
使用类型类如果:
每种给定类型只有一个正确的行为
类型类具有所有实例必须满足的关联方程(即“法则”)
嗯..
或者是类型类和数据/新类型有点相互竞争的概念共存的历史原因?
我认为类型类是Haskell的一个重要组成部分。
它们是Haskell的一部分,它使它成为我知道重构的最简单的语言,并且它们是您能够推断代码正确性的重要资产。
所以,我们来谈谈字典传递。
现在,任何类型的字典传递都是传统面向对象语言中事态的重大改进。 我们知道如何用C ++中的vtables进行OOP操作。 但是,VOP是OOP语言中的“对象的一部分”。 将vtable与对象结合起来会迫使你的代码变成一种形式,在这种形式下你可以严格规定谁可以用新特性来扩展核心类型,它实际上只是这个类的原作者,他必须将其他人想要烘焙的所有东西他们的类型。 这导致“熔岩流代码”和其他各种设计反模式等。
像C#这样的语言让你能够使用扩展方法来伪造新的东西,而在scala和其他语言中的多重继承等语言中的“特征”也可以委派一些工作,但它们是部分解决方案。
当你把虚拟表格从他们操作的对象中分离出来时,你会感到兴奋。 你现在可以将它们传递到你想要的任何地方,但是当然你需要命名它们并且谈论它们。 围绕模块/函子的ML规范和明确的字典传递样式采用了这种方法。
Typeclasses略有不同。 我们依赖于一个给定类型的类型实例的唯一性,并且这个选择在很大程度上允许我们摆脱这种简单的核心数据类型。
为什么?
因为我们可以将字典的使用移动到使用网站,并且不必随身携带数据类型,我们可以依赖这样一个事实:当我们这样做时,代码的行为没有任何改变。
将代码机械转换为更复杂的手动传递的字典会使给定类型的字典失去独特性。 在程序中的不同位置传递字典现在会导致行为差异很大的程序。 您可能会也可能不会记住您的数据类型所构建的字典,如果您想要根据您的参数进行有条件的行为,那么就会带来一些负面影响。
对于像Set
这样简单的例子,你可以通过手动词典翻译逃脱。 价格似乎不高。 你必须在字典中烘烤,比如说,当你创建对象然后insert
/ lookup
,你想如何对Set
进行排序,只会保留你的选择。 这可能是你可以承担的成本。 当你将两个Set
起来的时候,当然,它会在你命令中获得。 也许你会选择较小的一个,然后将它插入较大的区域,但是这样的排序会变得很糟糕,所以您必须说出左边的,并且始终将其插入右边,或记录这种随意的行为。 为了提高“灵活性”,您现在被迫进入次优解决方案。
但Set
是一个微不足道的例子。 在那里,你可能会烘烤一个关于你正在使用哪个实例的类型的索引,这里只涉及到一个类。 当你想要更复杂的行为时会发生什么? 我们用Haskell做的事情之一就是使用monad变形金刚。 现在你有很多实例在浮动 - 你没有一个好的地方来存储它们, MonadReader
, MonadWriter
, MonadState
等都可以应用..有条件地,基于monad。 当你提升和交换时会发生什么,现在不同的事情可能会或可能不会适用?
为此进行明确的字典是很多工作,没有一个好的地方来存储它们,并且你要求用户采用全球程序转换来采用这种做法。
这些是类型类变得毫不费力的事情。
我相信你应该把它们用于一切吗?
不是由一个长镜头。
但我不同意这里的其他答复,他们对Haskell无关紧要。
Haskell是唯一提供这些语言的语言,至少对于我用这种语言进行思考的能力至关重要,并且这也是我认为Haskell在家的重要原因。
我同意这里的一些事情,在有规律的时候使用类型类,并且选择是明确的。
然而,我会质疑,如果你没有法律,或者如果选择不明确,你可能不知道如何对问题领域进行建模,并且应该寻找可以适用于类型类的东西甚至可能进入现有的抽象 - 当你最终找到解决方案时,你会发现你可以很容易地重用它。
在大多数情况下,Typeclasses是无关紧要的。 任何类型类代码都可以机械地转换为字典传递样式。 他们主要提供便利,有时还有一定的便利(参见kmett的回答)。
有时,类型类的单实例属性用于执行不变量。 例如,您无法安全地将Data.Set
转换为字典传递样式,因为如果您使用两个不同的Ord
字典插入两次,则可能会破坏数据结构的不变性。 当然,你仍然可以将任何工作代码转换为字典传递风格的工作代码,但是你不能破坏尽可能多的破坏代码。
法律是类型化的另一个重要文化方面。 编译器不强制执行法律,但Haskell程序员希望类型类遵循所有实例满足的规则。 这可以用来为某些功能提供更强的保证。 这种优势只能来自社区的惯例,而不是语言的正式属性。
回答问题的这一部分:
“类型类和数据/新类型有点相互竞争的概念”
编号类型类是类型系统的扩展,它允许您对多态参数进行约束。 像编程中的大多数事物一样,它们当然是语法糖[所以它们不是必需的,因为它们的使用不能被其他任何东西所替代]。 这并不意味着他们是多余的。 这意味着你可以使用其他语言工具来表达类似的东西,但是当你处理它时你会失去一些清晰度。 字典传递可以用于大部分相同的事情,但是它在类型系统中最终不那么严格,因为它允许在运行时改变行为(这也是一个很好的例子,你将使用字典传递而不是类型类)。
无论是否有类型类型,Data和newtype仍然意味着完全相同的事情:引入一个新类型,将data
作为新类型的数据结构,以及将newtype
作为类型的type
安全变体。