Scala的map()在映射到相同类型时是否应该有不同的行为?

时间:2011-04-15 15:04:06

标签: scala type-inference scala-collections implicit static-typing

在Scala Collections框架中,我认为使用map()时有一些行为违反直觉。

我们可以区分(不可变)集合上的两种转换。那些实现调用newBuilder来重新创建生成的集合,以及那些通过隐式CanBuildFrom来获取构建器的人。

第一个类别包含所有转换,其中包含的元素的类型不会更改。例如,它们是filterpartitiondroptakespan等。这些转换可以免费调用newBuilder和重新创建与调用它们相同的集合类型,无论具体如何:过滤List[Int]始终可以返回List[Int];过滤BitSet(或this article on the architecture of the collections framework中描述的RNA示例结构)可以始终返回另一个BitSet(或RNA)。我们称之为过滤转换

第二类转换需要CanBuildFrom更灵活,因为所包含元素的类型可能会发生变化,因此,集合本身的类型可能无法重用:a { {1}}不能包含BitSet s; String仅包含RNA个。此类转换的示例包括BasemapflatMapcollectscanLeft等。让我们称之为映射转换

现在讨论这个主要问题。无论集合的静态类型是什么,所有过滤转换都将返回相同的集合类型,而映射操作返回的集合类型可能会因静态类型而异。

++

现在,我明白为什么会这样。它解释为therethere。简而言之:基于静态类型插入隐式scala> import collection.immutable.TreeSet import collection.immutable.TreeSet scala> val treeset = TreeSet(1,2,3,4,5) // static type == dynamic type treeset: scala.collection.immutable.TreeSet[Int] = TreeSet(1, 2, 3, 4, 5) scala> val set: Set[Int] = TreeSet(1,2,3,4,5) // static type != dynamic type set: Set[Int] = TreeSet(1, 2, 3, 4, 5) scala> treeset.filter(_ % 2 == 0) res0: scala.collection.immutable.TreeSet[Int] = TreeSet(2, 4) // fine, a TreeSet again scala> set.filter(_ % 2 == 0) res1: scala.collection.immutable.Set[Int] = TreeSet(2, 4) // fine scala> treeset.map(_ + 1) res2: scala.collection.immutable.SortedSet[Int] = TreeSet(2, 3, 4, 5, 6) // still fine scala> set.map(_ + 1) res3: scala.collection.immutable.Set[Int] = Set(4, 5, 6, 2, 3) // uh?! ,并且根据其CanBuildFrom方法的实现,可能会也可能无法重新创建相同的集合类型。

现在我的唯一观点是,当我们知道我们正在使用映射操作产生一个具有相同元素类型(编译器可以静态确定)的集合时,我们可以模仿过滤的方式转换工作并使用集合的本机构建器。我们可以在映射到def apply(from: Coll)时重用BitSet,创建具有相同排序的新Int等。

然后我们会避免

的情况
TreeSet

不会以与

相同的顺序打印for (i <- set) { val x = i + 1 println(x) } 的递增元素
TreeSet

所以:

  • 你觉得改变映射转换的行为是个好主意吗?
  • 我忽略了哪些不可避免的警告?
  • 怎么可以实施?

我正在考虑类似for (i <- set; x = i + 1) println(x) 参数的东西,可能默认值为implicit sameTypeEvidence: A =:= B(或者更确切地说是null),可以在运行时使用它来提供更多信息implicit canReuseCalleeBuilderEvidence: B <:< A = null,它可以用来确定要返回的构建器的类型。

1 个答案:

答案 0 :(得分:1)

我再次查看它,我认为您的问题不是来自Scala集合的特定缺陷,而是TreeSet的缺少构建器。因为以下内容确实可以正常工作:

val list = List(1,2,3,4,5)
val seq1: Seq[Int] = list
seq1.map( _ + 1 ) // yields List

val vector = Vector(1,2,3,4,5)
val seq2: Seq[Int] = vector
seq2.map( _ + 1 ) // yields Vector

原因是TreeSet缺少专门的伴侣对象/构建器:

seq1.companion.newBuilder[Int]    // ListBuffer
seq2.companion.newBuilder[Int]    // VectorBuilder
treeset.companion.newBuilder[Int] // Set (oops!)

所以我的猜测是,如果您为RNA课程为这样的同伴做出适当的规定,您可能会发现mapfilter都可以按照您的意愿工作......?