Why does this flatMap fail? - scala

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)

Related

Scala F-Bounded Type Polymorphism

trait Account[T <: Account[T]]
case class BrokerAccount(total:BigDecimal) extends Account[BrokerAccount]
case class SavingsAccount(total:BigDecimal) extends Account[SavingsAccount]
Below function declaration and invocation works fine.
def foo1( xs: Array[T forSome { type T <: Account[T] }]):Array[T forSome { type T <: Account[T] }] = xs
foo1(Array(BrokerAccount(100),SavingsAccount(50)))
But below invocation gives compilation error.
def foo2( xs: List[T forSome { type T <: Account[T] }]):List[T forSome { type T <: Account[T] }] = xs
foo2(List(BrokerAccount(100),SavingsAccount(50)))
Error
Main.scala:14: error: type mismatch;
found : List[Product with Serializable with Main.Account[_ >: Main.SavingsAccount with Main.BrokerAccount <: Product with Serializable with Main.Account[_ >: Main.SavingsAccount with Main.BrokerAccount <: Product with Serializable]]]
required: List[T forSome { type T <: Main.Account[T] }] foo2(List(BrokerAccount(100),SavingsAccount(50)))
Can someone please explain me why compilation error occur in later case?
The key to the problem is variance - you're trying to return a contravariant value in covariant position (function return type).
Despite List type is covariant in its argument (trait List[+A]), this essentially means its values are contravariant (can be assigned to a List of supertypes):
val listOfSupers: List[_ >: Account[_]] = List(BrokerAccount(100), SavingsAccount(50))
What you're trying to return from the function foo2 is a complete contrary - List[_ <: Account[_]], thus the compiler error.
If instead of List you use Set there, which is invariant in its type parameter just like Array, everything will work fine.

Java interop - type inference failure with wildcard types

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.

Returning Set[Class[A]] over Set[Class[_]]

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.

Converting a Java EnumSet to an Array

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))

Underscores in type bounds on type constructors

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.