Open scabug opened 13 years ago
Imported From: https://issues.scala-lang.org/browse/SI-4198?orig=1 Reporter: Christian Krause (wookietreiber)
Christian Krause (wookietreiber) said: sry almost forgot:
Scala code runner version 2.8.1.final -- Copyright 2002-2010, LAMP/EPFL
java version "1.6.0_23" Java(TM) SE Runtime Environment (build 1.6.0_23-b05) Java HotSpot(TM) 64-Bit Server VM (build 19.0-b09, mixed mode)
@paulp said: This sort of thing is dangerous, but you can do something like this. (I can't settle for not returning the same collection type.)
import scala.collection.{ mutable, immutable, generic }
import generic._
class RichTrav[T, CC[X] <: Traversable[X]](coll: CC[T]) {
def narrow[U <: AnyRef](implicit cm: ClassManifest[U], bf: CanBuildFrom[CC[T], U, CC[U]]): CC[U] = {
val clazz = cm.erasure
val buf = bf(coll)
coll foreach { x =>
if (clazz.isAssignableFrom(x.asInstanceOf[AnyRef].getClass))
buf += x.asInstanceOf[U]
}
buf.result
}
}
class A
class B extends A
class C extends B
object Test {
implicit def mkRich[T, CC[X] <: Traversable[X]](coll: CC[T]) = new RichTrav[T, CC](coll)
def main(args: Array[String]): Unit = {
val xs: List[Any] = List(new A, new B, new C, List(1), 5, List(new A))
println(xs.narrow[B])
println(xs.toIndexedSeq.narrow[B])
println(xs.toBuffer.narrow[B])
}
}
But now you don't get unchecked warnings, not that people seem to pay much attention to them.
scala> xs.narrow[List[Int]].flatten.sum
java.lang.ClassCastException: A cannot be cast to java.lang.Integer
at scala.runtime.BoxesRunTime.unboxToInt(Unknown Source)
at scala.math.Numeric$$IntIsIntegral$$.plus(Numeric.scala:45)
at scala.collection.TraversableOnce$$$$anonfun$$sum$$1.apply(TraversableOnce.scala:322)
at scala.collection.LinearSeqOptimized$$class.foldLeft(LinearSeqOptimized.scala:113)
at scala.collection.immutable.List.foldLeft(List.scala:45)
at scala.collection.TraversableOnce$$class.sum(TraversableOnce.scala:322)
@lindydonna said: Given that there is a reasonable workaround, I am reassigning to "future release suggestions." Though I agree that this would be a nice enhancement, implementing it is not trivial.
Christian Krause (wookietreiber) said (edited on Dec 10, 2012 10:00:35 PM UTC): I figure this might work:
$ scala
Welcome to Scala version 2.9.2 (OpenJDK 64-Bit Server VM, Java 1.7.0_09).
Type in expressions to have them evaluated.
Type :help for more information.
scala> class Foo
defined class Foo
scala> class Bar extends Foo
defined class Bar
scala> class Baz extends Bar
defined class Baz
scala> val l = List(new Foo, new Bar, new Baz)
l: List[Foo] = List(Foo@10a44013, Bar@f6aa7ee, Baz@2337022a)
scala> implicit def RichList[A](l: List[A]) = new {
| def narrow[X <: A](implicit m: ClassManifest[X]) = l filter m.erasure.isInstance
| }
RichList: [A](l: List[A])java.lang.Object{def narrow[X <: A](implicit m: ClassManifest[X]): List[A]}
scala> l.narrow[Foo]
res0: List[Foo] = List(Foo@10a44013, Bar@f6aa7ee, Baz@2337022a)
scala> l.narrow[Bar]
res1: List[Foo] = List(Bar@f6aa7ee, Baz@2337022a)
scala> l.narrow[Baz]
res2: List[Foo] = List(Baz@2337022a)
... a little more concise than Pauls example.
Using the collection API I discovered one way to filter out a subtype of a collection (I'll use List in my example):
... so I have extracted all the Bars from a List[Foo] and now have a List[Bar].
It would be even nicer and shorter to write something like this:
For this to work, one would need a method signature like in the following example:
... but, as you can see, this won't work due to type erasure. I'm also not sure whether to use 'c: Class[B]' for the parameter is the right choice.
The question remains, whether there is a type-safe way to implement this filter method to have the advantage of using the short syntax I presented above to filter a List by a single type.
Best regards Christian Krause aka wookietreiber