为什么Haskell错过了“明显”的类型类
考虑面向对象的语言:
大多数来自面向对象编程背景的人都熟悉各种语言中常见且直观的界面,这些界面捕捉了Java的Collection & List界面的本质。 Collection是指不一定具有自然顺序/索引的对象集合。 List是一个具有自然顺序/索引的集合。 这些接口抽象了Java中的许多库数据结构,就像其他语言中的等价接口一样,并且需要对这些接口有深入的了解才能与大多数库数据结构有效地协作。
过渡到Haskell:
Haskell有一个类型类系统,类似于对象上的接口类型。 当类型考虑功能时,Haskell似乎具有关于Functors,Applicative,Monads等的精心设计的类型层次结构。 他们显然需要正确和抽象的类型类。 然而,当您查看许多Haskell的容器( List , Map , Sequence , Set , Vector )时,它们几乎都具有非常相似(或相同)的函数,但不会通过类型类来抽象。
一些例子:
null用于测试“ null ” length / size elem / member集合包含 empty和/或singleton用于默认构造 union联盟 () / diff设置差异 (!) / (!!)用于不安全索引(部分功能) (!?) / lookup安全索引(总功能) 如果我想使用上面的任何函数,但是我已经导入了两个或多个容器,我必须从导入的模块开始隐藏函数,或者明确地从模块导入必要的函数,或者限定导入的模块。 但是因为所有的功能都提供了相同的逻辑功能,所以它看起来很麻烦。 如果函数是从类型类定义的,而不是在每个模块中分别定义的,编译器的类型推断机制就可以解决这个问题。 只要它们共享类型类(即:让我们只使用Sequence而不是List来获得更好的随机访问效率),它也会使切换底层容器变得简单。
为什么Haskell没有Collection和/或Indexable类型的类来统一和概括这些函数?
部分原因是单子和箭是Haskell的新颖创新功能,而集合则相对更为平凡。 Haskell作为一门研究语言有着悠久的历史, 有趣的研究问题(设计monad实例和定义monads的通用操作)比“工业强度”抛光(定义容器API)获得更多的开发工作量。
部分原因是,这些类型来自三个不同的包(基础,容器和矢量),有三个独立的历史和设计师。 这使得他们的设计师难以协调提供任何单一类型的实例。
部分原因是,定义一个类型类来覆盖你提到的所有五个容器是非常困难的。 List,Sequence和Vector相对相似,但Map和Set具有完全不同的约束。 对于列表,序列和矢量,您需要一个简单的构造函数类,但对于无法工作的Set,因为Set需要元素类型上的Ord实例。 更糟的是,Map可以支持大多数的方法,但是它的单例函数需要两个参数,其余的只需要一个。
lens包提供了一些。
测试空,创建空容器这些都是由Control.Lens.Empty的AsEmpty类型类提供的。
按键/索引访问元素 。 来自Control.Lens.At的At和Ixed类型类。
检查集合类容器中的成员资格 。 Contains来自Control.Lens.At类型类。
将元素添加到序列容器中并将其删除 。 Control.Lens.Cons的Cons和Snoc类型类。
此外, Applicative类型类的pure方法通常可用于创建“单例”容器。 对于Haskell中不是函子/应用程序的东西,例如Set ,可能point Data.Pointed 。
正如其他答案指出的那样,Haskell倾向于使用不同的词汇。 但是,我不认为他们已经很好地解释了差异的原因。
在像Java这样的语言中,功能不是“一等公民”; 的确,匿名函数在最新版本中是可用的,但是这种类型的接口(Collection,Indexable,Interable等)是在此之前设计的。
这使得传递我们的代码非常繁琐,所以我们更倾向于将其他人的数据传递给我们的代码。 例如:
Iterable数据让我们for (Foo x : anIterable) { ... } ArrayAccess数据让我们编写一个anArrayAccess[anIndex] 这种风格也可以在实现生成器的OO语言中看到,因为这是我们在生成器中for yieldedElement in aGenerator: ...编写for yieldedElement in aGenerator: ...的另一种方式for yieldedElement in aGenerator: ...
Haskell采用不同的方法来处理类型类:我们希望我们的代码能够传递给其他人的数据。 一些(简化的)例子:
Functor s接受我们的代码并将其应用于他们“包含”的任何元素 Monad接受我们的代码并将其应用于某种“序列” Foldable s接受我们的代码并用它来“减少”它们的内容 Java只需要Iterable因为我们必须在我们的for循环中调用我们的代码,所以我们可以确保它的调用是正确的。 Haskell需要更具体的类型类,因为别人的代码会调用我们的,所以我们需要指定它应该如何调用; 是map , fold unfold等?
谢天谢地,类型系统帮助我们选择正确的方法;)
链接地址: http://www.djcxy.com/p/42961.html