Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

flatMap on Map with wildcard type parameter

I'am trying to write something like this:

trait Typed[T]

trait Test {

  def testMap: Map[Typed[_], Int]

  def test = testMap.flatMap {case (typed, size) => Seq.fill(size)(typed)}
}

But I get the following error:

error: no type parameters for method flatMap: (f: ((Typed[_], Int)) => Traversable[B])(implicit bf: scala.collection.generic.CanBuildFrom[scala.collection.immutable.Map[com.quarta.service.querybuilder.Typed[_],Int],B,That])That exist so that it can be applied to arguments (((Typed[_], Int)) => Seq[Typed[_0]] forSome { type _0 })
--- because ---
argument expression's type is not compatible with formal parameter type;
found   : ((Typed[_], Int)) => Seq[Typed[_0]] forSome { type _0 }
required: ((Typed[_], Int)) => Traversable[?B]
def test = testMap.flatMap {case (typed, size) => Seq.fill(size)(typed)}

This code works if change testMap type to:

def testMap: Map[Typed[Any], Int]

What is the difference and how I can solve my problem?

like image 942
txt_flow Avatar asked Jan 18 '23 12:01

txt_flow


2 Answers

If I understood your question correctly, the answer is: You can do it if Typed is covariant in T, i.e. trait Typed[+T].

Example

scala> :paste
// Entering paste mode (ctrl-D to finish)

class Typed[+T: Manifest] {
  override def toString = "Typed[" + implicitly[Manifest[T]].toString + "]"
}

trait Test {
  def testMap: Map[Typed[_], Int]

  def foo = testMap flatMap { case (t, s) => Seq.fill(s)(t) }
}

val bar = new Test { 
  def testMap = Map(new Typed[Double]() -> 3, new Typed[Int]() -> 5)
}

// Hit Ctrl-D

scala> bar.foo
res0: scala.collection.immutable.Iterable[Seq[Typed[Any]]] = List(Typed[Double], Typed[Double], Typed[Double], Typed[Int], Typed[Int], Typed[Int], Typed[Int], Typed[Int])

Note that I've made Typed a class in this example to get nicer output. You can of course stick with a trait.

Now, why is covariance needed here?

Covariance basically means that if A <: B then X[A] <: X[B]. So if you were declaring testMap as Map[Typed[Any], Int] while Typed were invariant, you were not allowed to pass in e.g. a Typed[Double] for a Typed[Any] even though Double <: Any. Here, the scala compiler seems to be replacing _ with Any in the covariant case (see extempore's comment for an elaboration on this).

For an explanation of the problem regarding the underscore, I'd refer to Luigi's answer though.

like image 171
fotNelton Avatar answered Jan 28 '23 21:01

fotNelton


I think the problem is you're trying to pattern match an anonymous function with an existential type parameter.

From the language spec, section 8.5 regarding pattern matching anonymous functions:

The expected type of such an expression must in part be defined. It must be either scala.Functionk[S1, ... , Sk, R] for some k > 0, or scala.PartialFunction[S1, R], where the argument type(s) S1, ... , Sk must be fully determined, but the result type R may be undetermined.

testMap is an existential type (see language spec 3.2.10). An existential type has the form T forSome {Q} where Q is a sequence of type declarations. You have used the special placeholder syntax, so the type Map[Typed[_], Int] is equivalent to Map[Typed[t] forSome { type t }, Int], which may make the error message make a bit more sense.

As for a solution, I guess it depends exactly what you're trying to do, which you don't say... :)

like image 25
Luigi Plinge Avatar answered Jan 28 '23 21:01

Luigi Plinge