1

Consider the following, which compiles:

  val f: String => Set[Integer] = ???
  val a: Set[String] = ???
  val b = a.flatMap(s => f(s))

Now, if I change the first line above as following, the code no longer compiles:

  val f: String => Set[_ <: Integer] = ???
  val a: Set[String] = ???
  val b = a.flatMap(s => f(s))

The error is the following:

/Foo.scala:31: error: no type parameters for method flatMap: (f: String => scala.collection.GenTraversableOnce[B])(implicit bf: scala.collection.generic.CanBuildFrom[scala.collection.immutable.Set[String],B,That])That exist so that it can be applied to arguments (String => scala.collection.immutable.Set[_ <: Integer])
[ERROR]  --- because ---
[ERROR] argument expression's type is not compatible with formal parameter type;
[ERROR]  found   : String => scala.collection.immutable.Set[_ <: Integer]
[ERROR]  required: String => scala.collection.GenTraversableOnce[?B]
[ERROR]   val b = a.flatMap(s => f(s))
[ERROR]             ^
[ERROR] /Foo.scala:31: error: type mismatch;
[ERROR]  found   : String => scala.collection.immutable.Set[_ <: Integer]
[ERROR]  required: String => scala.collection.GenTraversableOnce[B]
[ERROR]   val b = a.flatMap(s => f(s))
[ERROR]                       ^
[ERROR] /Foo.scala:31: error: Cannot construct a collection of type That with elements of type B based on a collection of type scala.collection.immutable.Set[String]. 
[ERROR]   val b = a.flatMap(s => f(s))

Why does a compile error result here (I don't understand the above compiler error message), and how should I fix it?

jonderry
  • 23,013
  • 32
  • 104
  • 171
  • See the answer to this question: http://stackoverflow.com/questions/6141701/more-scala-typing-issues – wheaties Jan 06 '15 at 19:44

1 Answers1

3

It basically means that Scala doesn't know what type you mean. Let's write something that works:

val b = a.flatMap[Integer, Set[Integer]](s => f(s))

There are two type parameters to flatMap. The second parameter is the final type you'll get. That is, b, above, will be of type Set[Integer]. It could well be declared as Set[_ <: Integer], but that might be confused with the declaration of f, so I decided to make it different in that example.

The first parameter is the type of elements that will go into that collection (that is, the elements that will be produced by s => f(s)). So let's go back to f:

val f: String => Set[_ <: Integer] = ???

The type of element produced is an unknown subtype of Integer -- and, being unknown, Scala can't figure out what to use. Or, in order words, it can't infer the type parameters for flatMap.

By the way, if you want to keep Set[_ <: Integer] all the way, you could do this:

val b = a.flatMap[Integer, Set[_ <: Integer]](s => f(s))

Or even, to obfuscate everything,

val b = a.flatMap[T forSome { type T <: Integer }, Set[_ <: Integer]](s => f(s))
Daniel C. Sobral
  • 295,120
  • 86
  • 501
  • 681
  • 1
    Thanks this works, but why does `flatMap` take two type parameters here, even though the docs only specify one? The signature of `flatMap` for `Set` appears to be `def flatMap[B](f: (A) ⇒ GenTraversableOnce[B]): Set[B]` – jonderry Jan 06 '15 at 20:12
  • @jonderry That's the "use case" signature: a fake type signature which is easier to read and explains what happens most of the time, but isn't actually real. If you expand the doc for `flatMap`, you'll see a "Full Signature" session that can also be expanded to show the full type signature of the method. – Daniel C. Sobral Jan 08 '15 at 23:01