Reputation: 8434
I am having difficulty making the implicit requirements of flatUnzip
work properly. Currently it seems the first requirement that A
is a Tuple2[CC1[T1], CC2[T2]]
is being ignored (and hence the sanity check fails to compile). Any suggestions here? When answering, please also explain what is wrong with my current attempt.
class MySeq[A](val _seq: Seq[A]) extends AnyVal {
def flatUnzip[T1, T2, CC1[T1], CC2[T2]](
implicit ev1: A =:= Tuple2[CC1[T1], CC2[T2]],
ev2: CC1[T1] <:< TraversableOnce[T1],
ev3: CC2[T2] <:< TraversableOnce[T2],
cbf1: CanBuildFrom[CC1[T1], T1, CC1[T1]],
cbf2: CanBuildFrom[CC2[T2], T2, CC2[T2]]
): (CC1[T1], CC2[T2]) = {
val foo: Seq[Tuple2[CC1[T1], CC2[T2]]] = _seq // sanity check fails
val list1 = cbf1()
val list2 = cbf2()
for ((xs, ys) <- _seq) {
list1 ++= xs
list2 ++= ys
}
return (list1.result, list2.result)
}
}
EDIT
I found that the following works, but only when the =:=
is applied in the direction as shown:
class MySeq[A](val _seq: Seq[A]) extends AnyVal {
def mapBy[B](func: A => B): Map[B, A] = _seq.map(x => (func(x), x)).toMap
def flatUnzip[T1, T2, CC1[T1], CC2[T2]](
implicit
ev1: Tuple2[CC1[T1], CC2[T2]] =:= A,
ev2: Seq[A] =:= Seq[Tuple2[CC1[T1], CC2[T2]]],
ev3: CC1[T1] <:< TraversableOnce[T1],
ev4: CC2[T2] <:< TraversableOnce[T2],
cbf1: CanBuildFrom[CC1[T1], T1, CC1[T1]],
cbf2: CanBuildFrom[CC2[T2], T2, CC2[T2]]
): (CC1[T1], CC2[T2]) = {
val list1 = cbf1()
val list2 = cbf2()
for ((xs, ys) <- _seq: Seq[Tuple2[CC1[T1], CC2[T2]]]) {
list1 ++= xs
list2 ++= ys
}
return (list1.result, list2.result)
}
}
However, replacing Seq[A] =:= Seq[Tuple2[CC1[T1], CC2[T2]]]
with Seq[Tuple2[CC1[T1], CC2[T2]]] =:= Seq[A]
or Tuple2[CC1[T1], CC2[T2]] =:= A
with A =:= Tuple2[CC1[T1], CC2[T2]]
causes problems. Can someone please explain why the order here matters, and why each of these A =:= B
relationships is needed to make this work?
Upvotes: 1
Views: 148
Reputation: 30453
You can fix it by changing the ev1
to:
implicit ev1: Seq[A] =:= Seq[Tuple2[CC1[T1], CC2[T2]]],
Just because you have evidence that A
is =:=
some type, doesn't mean that evidence works for Seq[A]
, too. So ask directly for the evidence you actually need.
You'll also need to change the for
comprehension to:
for ((xs, ys) <- _seq: Seq[Tuple2[CC1[T1], CC2[T2]]]) {
without the typecast to trigger a search, the compiler won't go looking for ev1
.
UPDATE:
As you point out in your edited question, those fixes aren't enough to make the code work. You found the additional fixes needed yourself, but why do they work?
It's because the evidence supplied by =:=
isn't just a dummy value that is ignored. =:=
extends Function1
, and what it does the function do? It converts from the type on the left to the type on the right. At runtime, it does nothing (it's an identity function); but at compile time, the function may need to be applied in order to convert the type on the left to the type on the right.
In your revised code, ev2
is used on the input, to convert _seq
to the right type. If you compile the code with -Xprint:typer
, you will see the explicit calls to ev2
, ev3
, and ev4
.
Oh, uh, what about ev1
? I'm not sure. That doesn't show up in the compiled code. I'm not seeing right now why it's needed. The compiler seems to need it as a stepping stone in order to derive ev2
.
UPDATE #2:
Aha! For ev2
, use <:<
, not =:=
. Then you don't need ev1
anymore.
Upvotes: 1