Problem with partial unification and map traversal?

In conjunction with experimenting with Cats, I’ve been testing out the -Ypartial-unification flag. It seems to break what seems like a simple use case when I try to traverse over a Map.

Does anybody know whether this an expected issue, or is it a bug in the unification code? If the latter, I’ll happily file an issue.

Here’s my test code:

import scala.concurrent._
import ExecutionContext.Implicits.global
object test {
  val m = Map("a" -> 2)
  Future.traverse(m) { a => Future(()) }
}

When I do scalac test.scala it succeeds, but I get the following failure with partial unification.

> scalac -Ypartial-unification test.scala
test.scala:5: error: inferred type arguments [Int,Nothing,[+B]Map[String,B]] do not conform to method traverse's type parameter bounds [A,B,M[X] <: TraversableOnce[X]]
  Future.traverse(m) { a => Future(()) }
         ^
test.scala:5: error: type mismatch;
 found   : Map[String,Int]
 required: M[A]
  Future.traverse(m) { a => Future(()) }

I can work around this by doing Future.traverse(m: Iterable[(String, Int)]) { … }, but this seems like either a bug or an unfortunate problem that will exist in the standard library if partial unification becomes standard.

1 Like

I can’t say whether this is an insurmountable consequence of using partial unification, but I think that things which break existing code are worth filing an issue for.

Seems reasonable: I’ll do that @Jasper-M.

It’s a bug in the signature of Future#traverse: the declaration M[X] <: TraversableOnce[X] is too strict. (For that matter, the use of a HK type at all is too strict.) With p-u on, your inferred M fails to satisfy the bound (the power of which this traverse does not need anyway!), and it won’t back out to try a different one.

I’m not sure whether Cats provides traversable for Map[K, ?] and applicative for Future, but if it does, I suggest using the Cats traverse function instead of stdlib Future.traverse, which will be declared with the correctly-inferring signature. This will more closely match what you’re trying to do anyway than you could hope from even a fixed Future.traverse.

Have you actually created an issue? I’m experiencing the same behavior on Scala 2.12.6, so, apparently, it wasn’t fixed yet. If yes, could you please post it here?