5

I am trying to use traverse (or sequence which is pretty much the same for my task) from cats library https://typelevel.org/cats/typeclasses/traverse.html . I want to traverse a List[A] with function A => Either[L,R] to get Either[L,List[R]] as a result.

Consider the following tiny example (I use scala-2.12.6, cats-core-1.3.1, sbt-1.1.2):

import cats.implicits._

def isOdd(i: Int): Either[String, Int] = 
  if (i % 2 != 0) Right(i) else Left("EVEN")

val odd: Either[String, List[Int]] = (1 to 10).toList.traverse(isOdd)

It doesn't compile, it gives:

no type parameters for method traverse: (f: Int => G[B])(implicit evidence$1: cats.Applicative[G])G[List[B]] exist so that it can be applied to arguments (Int => Either[String,Int])
[error]  --- because ---
[error] argument expression's type is not compatible with formal parameter type;
[error]  found   : Int => Either[String,Int]
[error]  required: Int => ?G[?B]
[error]     val odd: Either[String, List[Int]] = (1 to 10).toList.traverse(isOdd)

type mismatch;
[error]  found   : Int => Either[String,Int]
[error]  required: Int => G[B]
[error]     val odd: Either[String, List[Int]] = (1 to 10).toList.traverse(isOdd)
[error]                                                                   ^

could not find implicit value for evidence parameter of type cats.Applicative[G]
[error]     val odd: Either[String, List[Int]] = (1 to 10).toList.traverse(isOdd)
[error]                                                                   ^
pkozlov
  • 746
  • 5
  • 17
  • 3
    `(1 to 10).toList.traverse[({type L[A] = Either[String, A]})#L, Int](isOdd)` works. Can be shortened with kind projector to `(1 to 10).toList.traverse[Either[String, ?], Int](isOdd)` or you can use type alias. I don't know how to make it work without the type specified. General problem is that compiler doesn't infer either properly there as it has 2 holes and it is harder. Earlier there was traverseU variant that often was helpful but it was removed. – Łukasz Sep 07 '18 at 13:11
  • Thank you very much, indeed it works. Why did you reply as a comment, not as an answer? It is more readable with type alias, indeed, like `type EitherString[R] = Either[String,R]` or something else instead of `String`. – pkozlov Sep 07 '18 at 15:50
  • 3
    I can get `(1 to 10).toList.traverse(isOdd)` to compile here. Are you sure you have enabled partial unification? (add `scalacOptions += "-Ypartial-unification"` to builld.sbt, see https://typelevel.org/cats/) – Thomas Dufour Sep 08 '18 at 08:37

1 Answers1

3

The Partial Unification compiler flag is required. In scala 2.12

add scalacOptions += "-Ypartial-unification" in build.sbt

Thanks Thomas

Stephen
  • 4,228
  • 4
  • 29
  • 40