我正在和熟人Komlin,Clojure和Java8粉丝交换电子邮件,并问他为什么不是Scala。他提供了很多理由(Scala太学术化,功能太多,不是我第一次听到这个,我认为这是非常主观的) 但他最大的痛点就是一个例子,他不喜欢一种他无法理解基本数据结构实现的语言,他以LinkedList为例。
我看了scala.collection.LinkedList
并计算了我理解或理解的事情。
但后来我开始盯着这些
能够理解这一点的人,请在LinkedList的上下文中解释GenericTraversableTemplate
SeqFactory
和GenericCompanion
吗?它们的用途是什么,对最终用户有什么影响(例如,我确定它们存在的原因很充分,这是什么原因?)
他们出于实际原因吗?或者它是一个可以简化的抽象层次?
我喜欢Scala系列,因为我不必理解内部能够有效地使用它们。如果它能帮助我保持用法更简单,我不介意复杂的实现。例如如果我能够使用它来编写更清晰,更优雅的代码,我不介意支付复杂库的价格。但是更好地理解它肯定会很好。
[1] - Is the Scala 2.8 collections library a case of "the longest suicide note in history"?
答案 0 :(得分:4)
我将尝试从随机行人的角度来描述这些概念(我从来没有为Scala收藏库贡献一条线,所以如果我和#39不要打我太厉害;我错了。)
由于LinkedList
现已弃用,并且由于地图提供了更好的示例,因此我将使用TreeMap
作为示例。
<强> CanBuildFrom 强>
动机是这样的:如果我们采用TreeMap[Int, Int]
并将其与
case (x, y) => (2 * x, y * y * 0.3d)
我们得到TreeMap[Int, Double]
。仅这种类型的安全性已经解释了必要性
简单的genericBuilder[X]
构造。
但是,如果我们用
case (x, y) => x
我们获得Iterable[Int]
(更确切地说:a List[Int]
),这不再是Map,容器的类型已经改变。这就是CBF发挥作用的地方:
CanBuildFrom[This, X, That]
可以看作是一种&#34;类型级功能&#34;这告诉我们:如果我们映射一个类型的集合 这个函数返回类型X的值,我们可以构建一个。最具体的 CBF在编译时提供,在第一种情况下它将类似于
CanBuildFrom[TreeMap[_,_], (X,Y), TreeMap[X,Y]]
在第二种情况下,它将类似于
CanBuildFrom[TreeMap[_,_], X, Iterable[X]]
所以我们总是得到正确的容器类型。这种模式非常普遍。 每次你有一个通用的功能
foo[X1, ..., Xn](x1: X1, ..., xn: Xn): Y
结果 type Y取决于X1,...,Xn,你可以引入一个隐式参数as 如下:
foo[X1, ...., Xn, Y](x1: X1, ..., xn: Xn)(implicit CanFooFrom[X1, ..., Xn, Y]): Y
然后定义类型级函数X1,...,Xn - &gt; Y通过提供多个分段 隐含的CanFooFrom&#39;
<强> LinkedListLike 强>
在类定义中,我们看到如下内容:
TreeMap[A, B] extends SortedMap[A, B] with SortedMapLike[A, B, TreeMap[A, B]]
这是Scala表达所谓F-有界多态性的方法。
动机如下:假设我们有特征SortedMap[A, B]
的十几个(或至少两个......)实现。现在我们想要实现一个方法withoutHead
,它可以看起来
有点像这样:
def withoutHead = this.remove(this.head)
如果我们将实现移到SortedMap[A, B]
本身,我们能做的最好的就是:
def withoutHead: SortedMap[A, B] = this.remove(this.head)
但这是我们可以获得的最具体的结果类型吗?不,那太模糊了。
如果原始地图是TreeMap[A, B]
,我们希望返回TreeMap
如果原件是CrazySortedLinkedHashMap
,则CrazySortedLinkedHashMap
(或其他......)。
这就是为什么我们将实现移到SortedMapLike
,并为withoutHead
方法提供以下签名:
trait SortedMapLike[A, B, Repr <: SortedMap[A, B]] {
...
def withoutHead: Repr = this.remove(this.head)
}
现在因为TreeMap[A, B]
扩展了SortedMapLike[A, B, TreeMap[A, B]]
,结果类型为
withoutHead
是TreeMap[A,B]
。同样适用于CrazySortedLinkedHashMap
:我们得到确切的类型。在Java中,您可能必须返回SortedMap[A, B]
或覆盖每个子类中的方法(结果是Scala中功能丰富的特征的维护噩梦)
<强> GenericTraversableTemplate 强>
类型为:GenericTraversableTemplate[+A, +CC[X] <: GenTraversable[X]]
据我所知,这只是一个提供实现的特性
以某种方式返回具有相同容器类型的常规集合的方法
可能不同的内容类型(flatten
,transpose
,unzip
等内容。
foldLeft
,reduce
,exists
之类的内容不在此处,因为这些方法只关注内容类型,而不关心容器类型。
像flatMap
这样的东西不在这里,因为容器类型可以改变(再次,CBF&#39;)。
为什么它是一个单独的特征,它存在的根本原因是什么? 我不这么认为......有可能将各种方法组合起来有所不同。但这正是自然发生的事情:你开始实现一个特征,事实证明它有很多方法。因此,你可以将松散相关的方法分组,并将它们放入10个不同的特征中,例如&#34; GenTraversableTemplate&#34;等尴尬的名称,然后将它们全部混合到需要它们的特征/类中......
<强> GenericCompanion 强>
这只是一个抽象类,它实现了一些常见的基本功能
对于大多数集合类的伴随对象(实质上,它只是非常实现
简单工厂方法apply(varargs)
和empty
)。
例如,方法apply
采用某种类型A的varargs
并返回类型为CC[A]
的集合:
Array(1, 2, 3, 4) // calls Array.apply[A](elems: A*) on the companion object
List(1, 2, 3, 4) // same for List
实施非常简单,它是这样的:
def apply[A](varargs: A*): CC[A] = {
val builder = newBuilder[A]
for (arg <- varargs) builder += arg
builder.result()
}
对于阵列和列表以及TreeMaps以及几乎所有其他内容,这显然是相同的,除了约束不规则的集合&#39;比如Bitset。所以这只是大多数伴随对象的共同祖先类中的常见功能。没什么特别的。
<强> SeqFactory 强>
与GenericCompanion类似,但这次更具体针对Sequences。
添加一些常见的工厂方法,例如fill()
和iterate()
以及tabulate()
等。
再说一次,这里没有特别的火箭科学......
一般性评论
总的来说:我不认为应该尝试理解这个库中的每个特征。相反,人们应该尝试将图书馆视为一个整体。 作为一个整体,它有一个非常有趣的架构。而在我个人看来,它实际上是一个非常美观的软件,但是人们必须盯着它看一段时间(并尝试多次重新实现整个架构模式)来掌握它。另一方面:例如CBF是一种&#34;设计模式&#34;显然应该在这种语言的后继者中消除。隐含CBF范围的整个故事对我来说似乎仍然是一场彻头彻尾的噩梦。但是很多事情一开始似乎完全不可思议,而且几乎总是以顿悟结束(这对于Scala来说非常具体:对于大多数其他语言而言,这种斗争通常以思想结束为止;#34的作者是完整的白痴&#34;。)