Scala .zip с типами с более высоким родом, странное поведение - PullRequest
3 голосов
/ 24 мая 2019

После вопроса и ответа в Методы Scala и параметры типа с более высоким родом , где я спросил, как создать метод с параметрами с более высоким родом, чтобы я мог использовать любой Seq, теперь я сталкиваюсь со странной проблемой при использовании метода .zip. Приведенный ниже код не компилируется, сообщает complire:

Error:(18, 28) type mismatch;
 found   : Seq[X]
 required: S[X]
        itrA.zip(itrB).map {

Я не знаю, где S [X] преобразуется в Seq [X]. Однако, если я заменю код вызовом .zip на более простой (закомментированный), который просто суммирует обе главы itrA и itrB и возвращает ItrA, компиляция будет успешной.

Код:

object SeqOps {

  def sum[X: Numeric, S[Y] <: Seq[Y]](s: Seq[S[X]])
    (implicit cbf: CanBuildFrom[Nothing, X, S[X]]): Seq[X] =

  /*
      This code compiles
       s.reduce{(itrA, itrB) =>
          val num = implicitly[Numeric[X]]
          val sum = new num.Ops(itrA.head).+(itrB.head)
          itrA
          */

      s.reduce{(itrA, itrB) =>
        itrA.zip(itrB).map { // it seems that itrB loses the type here :/
          case (a, b) =>
            val num = implicitly[Numeric[X]]
            val sum = new num.Ops(a).+(b)
            sum
      }

  }

  def main(args: Array[String]): Unit = {
    sum(Seq(Vector(1), Vector(1)))
  }

}

Вопросы: почему это происходит? Как я могу это исправить?

1 Ответ

1 голос
/ 25 мая 2019

Он не знает, что если вы zip два S[X], то он может каким-то образом построить из него S[(X, X)].Чтобы это работало, вам нужен CBF, который знает, как построить S[(X, X)].

К сожалению, не существует каких-либо последствий, которые могли бы построить, например, Vector[Int] изуниверсальный Seq[_], поэтому вам нужно больше информации о типе коллекции, из которой вы строите.Идея состоит в том, что черты с именем -Like могут предоставить эту информацию.В этом случае SeqLike[X, Repr] содержит тип Repr.

Если вы теперь достаточно долго смотрите на подпись zip в SeqLike:

def zip
  [A1 >: A, B, That]
  (that: GenIterable[B])
  (implicit bf: CanBuildFrom[Repr, (A1, B), That])
: That

, то вы увидите, что можете дать емуCBF с Repr в первом компоненте.Так что вы можете попробовать что-то вроде этого (примечание with SeqLike и два CBF с - один для zip, другой для map):

import scala.collection.generic.CanBuildFrom
import scala.language.higherKinds
import scala.collection.SeqLike

object SeqOps {


  def sum[X: Numeric, S[Y] <: Seq[Y] with SeqLike[Y, S[Y]]]
    (s: Seq[S[X]])
    (implicit 
      cbf1: CanBuildFrom[S[_], X, S[X]],
      cbf2: CanBuildFrom[S[_], (X, X), S[(X, X)]]
    )
  : Seq[X] = {
    val num = implicitly[Numeric[X]]
    import num._
    s.reduce(_.zip(_)(cbf2).map{ case (a, b) => a + b })
  }


  def main(args: Array[String]): Unit = {
    println(sum(Seq(Vector(1), Vector(1))))     // Vector(2)
    println(sum(Seq(List(2, 3), List(4, 5))))   // List(6, 8)
  }

}

Еще одно замечание: в некоторых случаях этопроще думать о Nothing как о Unobtanium.Если вашему CBF требуется Unobtanium для построения S[X], это, вероятно, ни к чему не годится, потому что, где вы хотите получить Unobtanium?

...