Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cannot prove that Unit <:< (T, U)

Tags:

scala

When trying to remove all Unit - () from a list, I tried to call toMap.

scala> List((), ()).filter(_ != ()).toMap
<console>:8: error: Cannot prove that Unit <:< (T, U).
              List((), ()).filter(_ != ()).toMap
                                           ^

What does this error mean?

For a List, I'd like to create a map of all tuples (String, String) for non-Unit elements, but some of the values can be null.

scala> val x = List((), (), (3,4)).filter(_ != ()).toMap
<console>:7: error: Cannot prove that Any <:< (T, U).
       val x = List((), (), (3,4)).filter(_ != ()).toMap
                                                   ^

scala> val x = List((), (), (3,4)).filter(_ != ())
x: List[Any] = List((3,4))

scala> x.toMap
<console>:9: error: Cannot prove that Any <:< (T, U).
              x.toMap
                ^
like image 806
Kevin Meredith Avatar asked Oct 10 '13 18:10

Kevin Meredith


3 Answers

Ah! Now your other question makes a little more sense. Still not sure what you're doing to produce this mixed Unit/Tuple2 list though.

This should work:

List((), (), (3,4)).collect { case t@(_: Int, _: Int) => t }.toMap

Note that I'm using variable binding here (binding the match to t) to return the same Tuple2 instance we matched rather than creating a new one.

By using collect you convert the type of your list from List[Any] to List[(Int, Int)], which is what toMap wants since it's expecting some List[(A,B)].


Note: Although this answer should work for you, I still think your design is flawed. You'd be better off fixing the underlying design flaw rather than treating the symptoms like this.

It looks like this would be a good fit for using Scala's Option type. In this case, your sample list would become List(None, None, Some((3,4))), or you could write it as List(None, None, Some(3->4)) for readability (nested parenthesis like that can get confusing).

If you use Option then the type of your list becomes List[Option[(Int, Int)]], which should be much nicer to deal with than a List[Any]. To get rid of the None entries and get the desired List[(Int,Int)] you can just call flatten:

List(None, None, Some(3->4)).flatten
// res0: List[(Int, Int)] = List((3,4))
List(None, None, Some(3->4)).flatten.toMap
// res1: scala.collection.immutable.Map[Int,Int] = Map(3 -> 4)

However, it would be even better if you can avoid putting the None entries in your list in the first place. If you're producing this list using a Scala for comprehension, you could use a guard in your for expression to remove the invalid elements from the output.

like image 88
DaoWen Avatar answered Nov 10 '22 22:11

DaoWen


It means that the type of an element in the list can't be viewed as a tuple which is required to build a Map. A Map in a sense is a collection of tuples (and more).

Illustration:

scala> List(1).toMap
<console>:8: error: Cannot prove that Int <:< (T, U).
          List(1).toMap
                  ^
scala> List(1 -> 2).toMap
res1: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2)

I can build a map from a list of tuples, but not from a list of single cardinality elements.

Maybe you mean to say .map instead of .toMap? ;)

like image 28
yǝsʞǝla Avatar answered Nov 10 '22 23:11

yǝsʞǝla


All in one go:

scala> val l2 = List(1 -> 3, (), 4 -> 4, (), 9 -> 4, (), 16 -> 7)
l2: List[Any] = List((1,3), (), (4,4), (), (9,4), (), (16,7))

scala> (l2 collect { case (a, b) => (a, b) }).toMap
res4: scala.collection.immutable.Map[Any,Any] = Map(1 -> 3, 4 -> 4, 9 -> 4, 16 -> 7)

Better typed:

scala> (l2 collect { case (i: Int, j: Int) => (i, j) }).toMap
res5: scala.collection.immutable.Map[Int,Int] = Map(1 -> 3, 4 -> 4, 9 -> 4, 16 -> 7)
like image 1
Randall Schulz Avatar answered Nov 10 '22 21:11

Randall Schulz