I'm trying to make a converter:
scala> implicit def enumSetToArray[T : ClassTag](enumSet: EnumSet[T]): Array[T] = enumSet.toArray[T](new Array[T](enumSet.size()))
<console>:9: error: type mismatch;
found : Array[T]
required: Array[T with Object]
Note: T >: T with Object, but class Array is invariant in type T.
You may wish to investigate a wildcard type such as `_ >: T with Object`. (SLS 3.2.10)
implicit def enumSetToArray[T : ClassTag](enumSet: EnumSet[T]): Array[T] = enumSet.toArray[T](new Array[T](enumSet.size()))
^
<console>:9: error: type mismatch;
found : Array[T with Object]
required: Array[T]
Note: T with Object <: T, but class Array is invariant in type T.
You may wish to investigate a wildcard type such as `_ <: T`. (SLS 3.2.10)
implicit def enumSetToArray[T : ClassTag](enumSet: EnumSet[T]): Array[T] = enumSet.toArray[T](new Array[T](enumSet.size()))
^
Ideas? My understanding is that I have to use the ClassTag to save the class from erasure so that reflection can instantiate the array, but apparently doing so messes with the variance.
Did you try
implicit def enumSetToArray[T <: Enum[T]](enumSet: EnumSet[T])(implicit ev: ClassTag[T]): Array[T] = enumSet.toArray[T](new Array[T](enumSet.size))
Related
I worked with TypeTags and noticed very strange scala compiler behaviour
import scala.reflect.runtime.universe.TypeTag
def accept[T](arg : TypeTag[T]) = true
def pass[T](arg : TypeTag[T]) : TypeTag[T] = arg
val ntt = implicitly[TypeTag[Nothing]]
accept(ntt)
pass(ntt)
returns error:
NastyNothing.scala:12: error: type mismatch;
found : reflect.runtime.universe.TypeTag[Nothing]
required: reflect.runtime.universe.TypeTag[T]
Note: Nothing <: T, but trait TypeTag is invariant in type T.
You may wish to investigate a wildcard type such as `_ <: T`. (SLS 3.2.10)
pass(ntt)
^
I could accept TypeTag of Nothing but could not pass it around. Moreover it fails similar with any object parametrized by the Nothing type.
final case class Hold[T]()
def accept[T](arg : Hold[T]) = true
def pass[T](arg : Hold[T]) : Hold[T] = arg
val hn = Hold[Nothing]()
accept(hn)
pass(hn)
return the same error:
NastyNothing.scala:24: error: type mismatch;
found : TestHolder.Hold[Nothing]
required: TestHolder.Hold[T]
Note: Nothing <: T, but class Hold is invariant in type T.
You may wish to define T as +T instead. (SLS 4.5)
pass(hn)
^
What is proper way to handle Nothing-parametrized objects? How should the pass method be redefined to process TypeTag[Nothing] correctly?
Is there any way to make this compile (without explicitly writing types)?
import java.util.{function => juf}
def jfun[A,B](f: A => B): juf.Function[A,B] =
new juf.Function[A,B] { def apply(a: A): B = f(a) }
val l = new java.util.ArrayList[String]
l.stream.map(jfun(_.toInt))
Result:
error: no type parameters for method map: (x$1: java.util.function.Function[_ >: String, _ <: R])java.util.stream.Stream[R] exist so that it can be applied to arguments (java.util.function.Function[String,Int])
--- because ---
argument expression's type is not compatible with formal parameter type;
found : java.util.function.Function[String,Int]
required: java.util.function.Function[_ >: String, _ <: ?0R]
Note: String <: Any, but Java-defined trait Function is invariant in type T.
You may wish to investigate a wildcard type such as `_ <: Any`. (SLS 3.2.10)
l.stream.map(jfun(_.toInt))
^
<console>:18: error: type mismatch;
found : java.util.function.Function[String,Int]
required: java.util.function.Function[_ >: String, _ <: R]
l.stream.map(jfun(_.toInt))
^
Is there any better explanation to why this doesn't work than "scala simply can't do it"?
PS. I know about workarounds like adding extension map method on Stream - I nevertheless would like to hear some explanation for this type inference limitation.
On Scala 2.10.4, given the following trait and case classes:
scala> trait Parent
defined trait Parent
scala> case class Girl() extends Parent
defined class Girl
scala> case class Boy() extends Parent
defined class Boy
I'm trying to define a method, f, that produces a Set[Class[A]] where A's type is A <: Parent.
scala> def f[A <: Parent]: Set[Class[A]] = Set[Class[A]](classOf[Boy], classOf[Girl])
<console>:12: error: type mismatch;
found : Class[Boy](classOf[$Boy])
required: Class[A]
def f[A <: Parent]: Set[Class[A]] = Set[Class[A]](classOf[Boy], classOf[Girl])
^
<console>:12: error: type mismatch;
found : Class[Girl](classOf[$Girl])
required: Class[A]
def f[A <: Parent]: Set[Class[A]] = Set[Class[A]](classOf[Boy], classOf[Girl])
But, I can make it work if I use, what I believe is the "wildcard":
scala> def g[A <: Parent]: Set[Class[_]] = Set[Class[_]](classOf[Boy], classOf[Girl])
g: [A <: Parent]=> Set[Class[_]]
And it works:
scala> g
res5: Set[Class[_]] = Set(class Boy, class Girl)
Why did the first approach fail, but the second succeeded? Lastly, is there any risk (to type safety) using Class[_] in the above definition of g?
The problem is that Class[A] is invariant in its type parameter. So Class[Boy] is not a Class[Parent]. The compiler will warn you of this if you set an explicit return type of Set[Class[Parent]].
scala> def f[_ <: Parent]: Set[Class[Parent]] = Set(classOf[Boy], classOf[Girl])
<console>:24: error: type mismatch;
found : Class[Boy](classOf[$Boy])
required: Class[Parent]
Note: Boy <: Parent, but Java-defined class Class is invariant in type T.
You may wish to investigate a wildcard type such as `_ <: Parent`. (SLS 3.2.10)
def f[_ <: Parent]: Set[Class[Parent]] = Set(classOf[Boy], classOf[Girl])
The following method uses existential types, which essentially means you don't care what the type is. _ is a completely unbound, and the type parameter A is superfluous.
def g[A <: Parent]: Set[Class[_]] = Set[Class[_]](classOf[Boy], classOf[Girl])
You might as well write:
def g: Set[Class[_]] = Set[Class[_]](classOf[Boy], classOf[Girl])
Lastly, is there any risk (to type safety) using Class[_] in the above definition of g?
Type safety has pretty much gone out the window at this point, because _ can be anything. Boy, Girl, Parent, Any, Toaster, ...
I think the best you can hope for is to heed the warning and get something like this:
def g: Set[Class[_ <: Parent]] = Set(classOf[Boy], classOf[Girl])
This will at least ensure that you have a Set with elements bounded above by Parent. Exactly what you intend to do with it, I don't know.
scala> val s: Seq[Class[_ <: java.lang.Enum[_]]] = Seq(classOf[java.util.concurrent.TimeUnit])
s: Seq[Class[_ <: java.lang.Enum[_]]] = List(class java.util.concurrent.TimeUnit)
scala> s.flatMap(_.getEnumConstants)
<console>:9: error: no type parameters for method flatMap: (f: Class[_ <: java.lang.Enum[_]] => scala.collection.GenTraversableOnce[B])(implicit bf: scala.collection.generic.CanBuildFrom[Seq[Class[_ <: java.lang.Enum[_]]],B,That])That exist so that it can be applied to arguments (Class[_ <: java.lang.Enum[_]] => scala.collection.mutable.ArrayOps[_$1(in value $anonfun) with java.lang.Object] forSome { type _$1(in value $anonfun) <: java.lang.Enum[_] })
--- because ---
argument expression's type is not compatible with formal parameter type;
found : Class[_ <: java.lang.Enum[_]] => scala.collection.mutable.ArrayOps[_$1(in value $anonfun) with java.lang.Object] forSome { type _$1(in value $anonfun) <: java.lang.Enum[_] }
required: Class[_ <: java.lang.Enum[_]] => scala.collection.GenTr... s.flatMap(_.getEnumConstants)
Not really the precise answer, but two observations - Scala 2.10 will give you a nicer error:
scala> s.flatMap(_.getEnumConstants)
<console>:9: error: no type parameters for method flatMap: (f: Class[_ <: Enum[_]] => scala.collection.GenTraversableOnce[B])(implicit bf: scala.collection.generic.CanBuildFrom[Seq[Class[_ <: Enum[_]]],B,That])That exist so that it can be applied to arguments (Class[_ <: Enum[_]] => scala.collection.mutable.ArrayOps[(some other)_$1(in object $iw) with Object] forSome { type (some other)_$1(in object $iw) <: Enum[_] })
--- because ---
argument expression's type is not compatible with formal parameter type;
found : Class[_ <: Enum[_]] => scala.collection.mutable.ArrayOps[(some other)_$1(in object $iw) with Object] forSome { type (some other)_$1(in object $iw) <: Enum[_] }
required: Class[_ <: Enum[_]] => scala.collection.GenTraversableOnce[?B]
s.flatMap(_.getEnumConstants)
^
<console>:9: error: type mismatch;
found : Class[_ <: Enum[_]] => scala.collection.mutable.ArrayOps[(some other)_$1(in object $iw) with Object] forSome { type (some other)_$1(in object $iw) <: Enum[_] }
required: Class[_ <: Enum[_]] => scala.collection.GenTraversableOnce[B]
s.flatMap(_.getEnumConstants)
^
<console>:9: error: Cannot construct a collection of type That with elements of type B based on a collection of type Seq[Class[_ <: Enum[_]]].
s.flatMap(_.getEnumConstants)
^
And, if you split your flatMap, you get to see a simpler version of the issue:
scala> s.map(_.getEnumConstants)
res28: Seq[Array[_$1 with Object] forSome { type _$1 <: Enum[_] }] = List(Array(NANOSECONDS, MICROSECONDS, MILLISECONDS, SECONDS, MINUTES, HOURS, DAYS), Array(RELEASE_0, RELEASE_1, RELEASE_2, RELEASE_3, RELEASE_4, RELEASE_5, RELEASE_6))
scala> res28.flatten
<console>:10: error: No implicit view available from Array[_$1 with Object] forSome { type _$1 <: Enum[_] } => scala.collection.GenTraversableOnce[B].
res28.flatten
^
That is rather surprising since you'd think that it should be easy to turn an Array into a GenTraversableOnce. I don't have time to dig out the details at the moment, but I'll point out that the following things seem to work:
s.flatMap(_.getEnumConstants.toSeq)
s.flatMap(_.getEnumConstants.map(_.asInstanceOf[Enum[_]]))
I vote compiler bug, because of this gist, which shows some very weird behaviour in the REPL for this simple script
val s: Seq[Class[_ <: java.lang.Enum[_]]] = Seq(classOf[java.util.concurrent.TimeUnit], classOf[javax.lang.model.SourceVersion])
s.flatMap(_.getEnumConstants.toSeq)
s.flatMap(_.getEnumConstants.toArray)
1234
TimeUnit.getEnumConstants returns a java array TimeUnit[], while flatMap expects a GenTraversable
You can get by with
scala> s.flatMap(_.getEnumConstants.toSeq)
res4: Seq[Enum[_]] = List(NANOSECONDS, MICROSECONDS, MILLISECONDS, SECONDS, MINUTES, HOURS, DAYS)
Can someone explain why the following doesn't compile? I want that BB[A] is also a List[A]. The method body only enforces this view.
scala> def x[A, BB[_] <: List[_]](p: BB[A]) {p: List[A]}
<console>:8: error: type mismatch;
found : BB[A]
required: List[A]
def x[A, BB[_] <: List[_]](p: BB[A]) {p: List[A]}
^
I think you need to name the _ parameter.
scala> def x[A, BB[X] <: List[X]](p: BB[A]) {p: List[A]}
works.