But the result of mapping a BitSet is not consistent with this behavior:
scala> val iter2: Iterable[Int] = BitSet(1, 2, 4)
iter2: Iterable[Int] = BitSet(1, 2, 4)
scala> iter2.map(identity)
res3: Iterable[Int] = Set(1, 2, 4)
Is this a design or an issue? Thanks.
The fix would be to percolate a special case for "same output as input type" throughout the constellation of implicit CanBuildFroms and the self-builder methods they call everywhere in the collections hierarchy. I cannot imagine the Scala devs adding such complexity just to provide this guarantee
--
You received this message because you are subscribed to the Google Groups "scala-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-user+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
not to know that "identity" maps non-negative Ints to non-negative Ints
On Monday, July 20, 2015 at 8:34:24 PM UTC-4, Rex Kerr wrote:not to know that "identity" maps non-negative Ints to non-negative IntsEh? The question of sign doesn't bother it in the case where the precise type is known at compile-time:
Let’s look at the machinery in more detail by progressively expanding and inlining the two cases:
scala> (List(1): Iterable[Int]).map(identity)
res15: Iterable[Int] = List(1)
scala> (List[Int](1): Iterable[Int]).map[Int, Iterable[Int]](identity[Int])(Iterable.canBuildFrom[Int])
res16: Iterable[Int] = List(1)
scala> val self: Iterable[Int] = List(1); val cbf = Iterable.canBuildFrom[Int]; val builder = cbf.apply(temp); builder += identity(self.head); builder.result()
self: Iterable[Int] = List(1)
cbf: scala.collection.generic.CanBuildFrom[Iterable.Coll,Int,Iterable[Int]] = scala.collection.generic.GenTraversableFactory$anon$1@3212d853
builder: scala.collection.mutable.Builder[Int,Iterable[Int]] = ListBuffer(1)
res17: Iterable[Int] = List(1)
scala> val self: Iterable[Int] = List(1); val cbf = Iterable.canBuildFrom[Int]; val builder = temp.genericBuilder[Int]; builder += identity(self.head); builder.result()
self: Iterable[Int] = List(1)
cbf: scala.collection.generic.CanBuildFrom[Iterable.Coll,Int,Iterable[Int]] = scala.collection.generic.GenTraversableFactory$anon$1@3212d853
builder: scala.collection.mutable.Builder[Int,Iterable[Int]] = ListBuffer(1)
res18: Iterable[Int] = List(1)
Things are similar for BitSet
:
scala> val self: Iterable[Int] = BitSet(1); val cbf = Iterable.canBuildFrom[Int]; val builder = temp.genericBuilder[Int]; builder += identity(self.head); builder.result()
self: Iterable[Int] = BitSet(1)
scala> val self: Iterable[Int] = BitSet(1); val cbf = Iterable.canBuildFrom[Int]; val builder = cbf.apply(self); builder += identity(self.head); builder.result()
self: Iterable[Int] = BitSet(1)
cbf: scala.collection.generic.CanBuildFrom[Iterable.Coll,Int,Iterable[Int]] = scala.collection.generic.GenTraversableFactory$anon$1@3212d853
builder: scala.collection.mutable.Builder[Int,Iterable[Int]] = scala.collection.mutable.SetBuilder@5eac24de
res29: Iterable[Int] = Set(1)
scala> val self: Iterable[Int] = BitSet(1); val cbf = Iterable.canBuildFrom[Int]; val builder = self.genericBuilder[Int]; builder += identity(self.head); builder.result()
self: Iterable[Int] = BitSet(1)
cbf: scala.collection.generic.CanBuildFrom[Iterable.Coll,Int,Iterable[Int]] = scala.collection.generic.GenTraversableFactory$anon$1@3212d853
builder: scala.collection.mutable.Builder[Int,Iterable[Int]] = scala.collection.mutable.SetBuilder@7fc9253c
res30: Iterable[Int] = Set(1)
The key difference is:
scala> List(1).genericBuilder[Int]
res32: scala.collection.mutable.Builder[Int,List[Int]] = ListBuffer()
scala> BitSet(1).genericBuilder[Int]
res33: scala.collection.mutable.Builder[Int,scala.collection.immutable.Set[Int]] = scala.collection.mutable.SetBuilder@23bd7935
Noting the interplay between GenericBuilder and GenTraversableTemplate#genericBuilder.
BitSet
can’t offer to generically map to another BitSet
, it can only do that with an implicit witness of that the resulting element type is an Int
. Instead, it has to build the a truly generic collection, Set
.
-jason
On Monday, July 20, 2015 at 7:59:52 PM UTC-4, Stephen Compall wrote:The fix would be to percolate a special case for "same output as input type" throughout the constellation of implicit CanBuildFroms and the self-builder methods they call everywhere in the collections hierarchy. I cannot imagine the Scala devs adding such complexity just to provide this guarantee
Nonetheless, the guarantee is provided. I cannot find a concrete collection other than immutable.BitSet that does *not* behave in accordance with this guarantee. (I didn’t exhaustively test them all, but I tried a dozen.)
Welcome to Scala version 2.11.7 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_45). Type in expressions to have them evaluated. Type :help for more information. scala> Map(1 -> 2) res0: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2) scala> (res0: Iterable[(Int,Int)]) map identity res1: Iterable[(Int, Int)] = List((1,2)) scala> collection.immutable.TreeSet(1, 2) res2: scala.collection.immutable.TreeSet[Int] = TreeSet(1, 2) scala> (res2: Iterable[Int]) map identity res3: Iterable[Int] = Set(1, 2) scala> 1 to 2 res4: scala.collection.immutable.Range.Inclusive = Range(1, 2) scala> (res4: Iterable[Int]) map identity res5: Iterable[Int] = Vector(1, 2) scala> object Blah extends Enumeration {val one, two = Value} defined object Blah scala> Blah.values res6: Blah.ValueSet = Blah.ValueSet(one, two) scala> (res6: Iterable[Blah.Value]) map identity res7: Iterable[Blah.Value] = Set(one, two) scala> Array("one", "two") res8: Array[String] = Array(one, two) scala> (Array("one", "two"): Iterable[String]) map identity res9: Iterable[String] = ArrayBuffer(one, two) scala> "xyz" map identity res10: String = xyz scala> ("xyz": Iterable[Char]) map identity res11: Iterable[Char] = Vector(x, y, z)Maybe the last two are unfair. But they are in the same mold as the others.
The mechanism that provides this guarantee is described in Odersky & Moors, “Fighting Bit Rot with Types (Experience Report: Scala Collections)”, http://lampwww.epfl.ch/~odersky/papers/fsttcs2009.pdf: "The idea is to give the apply method of CanBuildfrom access to the dynamic type of the original collection via its from argument..." It happens without any percolation of special cases throughout the hierarchy.
-- Stephen Compall If anyone from the MSA is online, they should watch this flythrough.
On Monday, July 20, 2015 at 8:34:24 PM UTC-4, Rex Kerr wrote:not to know that "identity" maps non-negative Ints to non-negative IntsEh? The question of sign doesn't bother it in the case where the precise type is known at compile-time:scala> collection.immutable.BitSet(1, 2, 3).map(_ + 1)res0: scala.collection.immutable.BitSet = BitSet(2, 3, 4)scala> collection.immutable.BitSet(1, 2, 3).map(_ * -1)java.lang.IllegalArgumentException: requirement failedat scala.Predef$.require(Predef.scala:207)at scala.collection.mutable.BitSet.add(BitSet.scala:83)If sign is considered a runtime question here, it should be also be considered a runtime question in the case where the precise type isn't known at compile time.