Jeff
Jeff

Reputation: 237

Type resolution with higher kinded types and implicits

I am trying to write a Value Class to add functionality to anything that implements Seq[_] and allow it to make batch calls that return a Future[_] (specifically, I am using it to make batch REST calls).

final class BatchedList[A, C[X] <: Seq[X]](val targetList: C[A]) extends AnyVal {

  def batchRequests[B](batchSize: Int)(runner: Seq[A] => Seq[Future[Either[Result, B]]])
    (implicit bf: CanBuildFrom[C[A], Either[Result, B], C[Either[Result, B]]]): Future[Either[Result, C[B]]] = {
      targetList.grouped(batchSize).foldLeft(Future.successful(bf(targetList))) { (results, set) =>
        results flatMap { responses =>
          Future.sequence(runner(set)).map(responses ++=)
        }
      } map {
        _.result().sequenceU
      }
    }
  }

However, I can't get this to compile. I keep receiving the compiler error

value sequenceU is not a member of type parameter C[Either[play.api.mvc.Result,B]]

I've imported both scalaz._ and Scalaz._, and I know they've provided a Traverse[_] for my use case (which is List[_] in this example). I'm pretty sure this is some sort of implicit resolution issue with the types, but I'm stumped on how to proceed forward resolving it.

Upvotes: 0

Views: 121

Answers (1)

Hugh
Hugh

Reputation: 8932

I believe that this happens because Scalaz doesn't provide typeclass instances for Seq but instead for IndexedSeq and List. As such, you'll need to provide the Traverse instance yourself (note the additional implicit argument C to batchRequests):

final class BatchedList[A, C[X] <: Seq[X]](val targetList: C[A]) extends AnyVal {
  def batchRequests[B](batchSize: Int)(runner: Seq[A] => Seq[Future[Either[Result, B]]])
    (implicit bf: CanBuildFrom[C[A], Either[Result, B], C[Either[Result, B]]], C: Traverse[C]): Future[Either[Result, C[B]]] =
      targetList.grouped(batchSize).foldLeft(Future.successful(bf(targetList))) { (results, set) =>
        results flatMap { responses =>
          Future.sequence(runner(set)).map(responses ++=)
        }
      } map {
        _.result().sequenceU
      }
}

As you can see, this will return a sequence type corresponding to the type provided as C:

scala> def run[A](s: Seq[A]): Seq[Future[Either[Result, A]]] =
     |   s.map(i => Future.successful(Right(i)))
run: [A](s: Seq[A])Seq[scala.concurrent.Future[Either[Result,A]]]

scala> :t new BatchedList(List(1,2,3)).batchRequests(1)(run)
scala.concurrent.Future[Either[Result,List[Int]]]

scala> :t new BatchedList(Vector(1,2,3)).batchRequests(1)(run)
scala.concurrent.Future[Either[Result,scala.collection.immutable.Vector[Int]]]

If you always want it to return a Seq it's a simple matter of an upcast.

Upvotes: 1

Related Questions