Scala - 列表元组列表的隐式证据

时间:2015-04-26 23:08:01

标签: scala implicit

我很难使flatUnzip的隐含要求正常工作。目前看来ATuple2[CC1[T1], CC2[T2]]的第一个要求被忽略(因此完整性检查无法编译)。这里有什么建议?在回答时,请解释我目前的尝试有什么问题。

  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)
    }
  }

修改

我发现以下情况有效,但仅当=:=按照所示方向应用时才会有效:

  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)
    }
  }

但是,将Seq[A] =:= Seq[Tuple2[CC1[T1], CC2[T2]]]替换为Seq[Tuple2[CC1[T1], CC2[T2]]] =:= Seq[A]Tuple2[CC1[T1], CC2[T2]] =:= A替换为A =:= Tuple2[CC1[T1], CC2[T2]]会导致问题。有人可以解释为什么这里的订单很重要,以及为什么需要这些A =:= B关系中的每一个才能使这项工作成功?

1 个答案:

答案 0 :(得分:1)

您可以通过将ev1更改为:

来解决此问题
implicit ev1: Seq[A] =:= Seq[Tuple2[CC1[T1], CC2[T2]]],

仅仅因为您有A =:=某种类型的证据,并不意味着证据也适用于Seq[A]。因此,请直接询问您实际需要的证据。

您还需要将for理解更改为:

for ((xs, ys) <- _seq: Seq[Tuple2[CC1[T1], CC2[T2]]]) {
如果没有用于触发搜索的类型转换,编译器就不会去寻找ev1

<强>更新

正如您在编辑过的问题中指出的那样,这些修补程序不足以使代码正常工作。您自己找到了所需的其他修补程序,但为什么它们有效?

这是因为=:=提供的证据不仅仅是一个被忽略的虚拟值。 =:=扩展Function1,它的功能是什么?它从左侧的类型转换为右侧的类型。在运行时,它什么都不做(它是一个身份函数);但是在编译时,可能需要应用该函数才能将左侧的类型转换为右侧的类型。

在修改后的代码中,输入使用ev2,将_seq转换为正确的类型。如果您使用-Xprint:typer编译代码,则会看到对ev2ev3ev4的显式调用。

哦,呃,ev1怎么样?我不确定。这并没有出现在已编译的代码中。我现在没有看到它为什么需要它。编译器似乎需要它作为跳板才能派生ev2

更新#2:

啊哈!对于ev2,请使用<:<,而不是=:=。那你就不再需要ev1了。