Kvass
Kvass

Reputation: 8434

Scala - implicit evidence of list of tuples of lists

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

Answers (1)

Seth Tisue
Seth Tisue

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

Related Questions