FlatMap on Map with a wildcard 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 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 you change the type of testMap to:

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

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

+4
source share
3 answers

If I understand your question correctly, the answer is as follows: you can do this if Typed covariant in T , that is, 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 made the Typed class in this example to get a better result. You can, of course, stick with trait .

Now, why is covariance necessary?

Covariance basically means that if A <: B , then X[A] <: X[B] . Therefore, if you declared testMap as Map[Typed[Any], Int] , and Typed were invariant, you were not allowed to pass, for example. a Typed[Double] for a Typed[Any] , although Double <: Any . Here the scala compiler seems to replace _ with Any in the covariant case (see extempore's comment for development on this).

To explain the underscore problem, I would name Luigi's answer.

+5
source

I think the problem is that you are trying to map an anonymous function to a parameter of an existential type.

From the language specification, section 8.5, on mapping patterns to anonymous functions:

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

testMap is an existential type (see language specification 3.2.10). The existential type has the form T forSome {Q} , where Q is the sequence of type declarations. You used special placeholder syntax, therefore the type Map[Typed[_], Int] equivalent to Map[Typed[t] forSome { type t }, Int] , which can make the error message more understandable.

As for the solution, I think it depends on what you are trying to do, what you are not saying ... :)

+4
source

Isn't that an option to parameterize a test?

 trait Test [A] { def testMap: Map [Typed [A], Int] def test = testMap.flatMap {case (typed, size) => Seq.fill (size)(typed)} } 

Do you need to declare a test before you know what will happen?

0
source

Source: https://habr.com/ru/post/1395825/


All Articles