具有类型边界的通用函数

时间:2014-08-29 20:49:49

标签: scala

我正在尝试编写一个泛型函数,它接受一个实现“++”的通用容器,但是我似乎无法正确使用语法。

def pagedRequest[A, C[_] <: Iterable](url: String, accumulator: C[A])(parser: (WSResponse) => C[A]): Future[Either[Result, C[A]]] = {
  WS.url(url).get().flatMap { response =>
    response.status match {
      case OK =>
        val data = accumulator ++ parser(response)

        (response.json \ "paging" \ "next").asOpt[String] match {
          case None => Future.successful(Right(data))
          case Some(next) => pagedRequest(next, data)(parser)
        }

      case _ =>
        Future.successful(Left(ProxiedResult(response)))
    }
  }
}

“数据”的类型保持可迭代[A],而不是C [A]。

2 个答案:

答案 0 :(得分:0)

您的问题是++运算符采用隐式CanBuildFrom,其类型为Iterable

来自Scala源中的TraversableLikeIterable

  def ++[B >: A, That](that: GenTraversableOnce[B])(implicit bf: CanBuildFrom[Repr, B, That]): That = {
    val b = bf(repr)
    if (that.isInstanceOf[IndexedSeqLike[_, _]]) b.sizeHint(this, that.seq.size)
    b ++= thisCollection
    b ++= that.seq
    b.result
  }

  implicit def canBuildFrom[A]: CanBuildFrom[Coll, A, Iterable[A]] = ReusableCBF.asInstanceOf[GenericCanBuildFrom[A]]

因此,++的结果为That,其类型为Iterable。我不认为你能做到你想做的事情而不会像那样丑陋......

答案 1 :(得分:0)

这样做的唯一方法似乎是明确的类型转换。

极简主义的例子:

1)没有类型转换,我们得到Iterable:

scala> def addup[A, C[A] <: Iterable[A]](a:C[A],b:C[A])=a ++ b
addup: [A, C[A] <: Iterable[A]](a: C[A], b: C[A])Iterable[A]

scala> addup(List(2,3,4,5),List(4,5,6,7))
res3: Iterable[Int] = List(2, 3, 4, 5, 4, 5, 6, 7)

2)使用类型转换,我们得到C [A]:

scala> def addupC[A, C[A] <: Iterable[A]](a:C[A],b:C[A])=(a ++ b).asInstanceOf[C[A]]
addupC: [A, C[A] <: Iterable[A]](a: C[A], b: C[A])C[A]

scala> addupC(List(2,3,4,5),List(4,5,6,7))
res4: List[Int] = List(2, 3, 4, 5, 4, 5, 6, 7)

(顺便说一下,当像Slick这样的库一起工作时会发生类似的问题。)

在你的情况下,你必须尝试这个::

def pagedRequest[A, C[A] <: Iterable[A]](url: String, accumulator: C[A])(parser: (WSResponse) => C[A]): Future[Either[Result, C[A]]] = {
  WS.url(url).get().flatMap { response =>
    response.status match {
      case OK =>
        val data = (accumulator ++ parser(response)).asInstanceOf[C[A]]

        (response.json \ "paging" \ "next").asOpt[String] match {
          case None => Future.successful(Right(data))
          case Some(next) => pagedRequest(next, data)(parser)
        }

      case _ =>
        Future.successful(Left(ProxiedResult(response)))
    }
  }
}