Something like this:
implicit class PairOps[A, B, C[X] <: Iterable[X]](val c: C[(A,B)]) extends AnyVal {
def swap = c.map { case (a, b) => (b, a) }
}
Kinda works ... except that val foo: Seq[(Int, String)] = Seq(("foo",1)).swap
does not compile, because swap
returns Iterable
rather than a Seq
.
How do I fix it? There used to be breakOut
in 2.12 that was using some magic (that I never quite understood tbh) to do this kind of thing ... but now I need a Factory
...
Tried adding it as an implicit param:
def swap(implicit f: Factory[(B,A),C]) = c.map { case (a,b) => (b,a) }.to(f) }
This compiles with the right type, but I can't use it, because I don't have that implicit in scope at the call site (even swap(Seq)
does't work for some reason, even though swap.to(Seq)
does (in the first version, without implicit factory) ...
Can someone please set me straight here? There must be a way to accomplish what I want here, but I can't figure out the right incantation :(
What works for me is modifying the implicit you are fetching:
import scala.collection.Factory
implicit class PairOps[A, B, C[X] <: Iterable[X]](val c: C[(A,B)]) extends AnyVal {
def swap(implicit f: Factory[(B, A), C[(B, A)]]) = c.map { case (a, b) => (b, a) }.to(f)
}
List(1 -> "test").swap
see Scastie
The reason for the verbosity is that ask for [A, C[_]]
and make C[A]
out of it approach which was used prior to 2.13 doesn't work if you want to do e.g.
Factory[(Int, String), Map[Int, String]]
(You can check that in 2.12 when you use .to(Map)
(e.g with scala-collection-compat) you'll end up with a Map... upcasted to Iterable[(K, V)]
precisely because .to
interface operates on [A, Coll[_]]
and to be able to shove Map
factory there, it has to upcast it to Iterable
factory.)
In other words the verbosity added by by 2.13 allows Factory
to be used in cases like seq.map(a => a -> a.toString).to(Map)
which eliminated the need for CanBuildFrom
and separate syntax for .to[Coll]
and .toMap
.