问题
I wrote the following simple code:
import cats.effect.IO
import cats.instances.either._
import cats.syntax.TraverseSyntax
object Test extends App with TraverseSyntax{
val e: Either[String, IO[Int]] = Right(IO(2))
e.sequence //error here
}
Unfortunately it refuses to compile with the
Error:(25, 94) value sequence is not a member of scala.util.Either
Can you please explain why? I imported either
instances which include Traverse[Either[A, ?]]
. What's wrong?
回答1:
Traverse[F]
is defined as a typeclass for a type with one type parameter F[T]
. Either
type has two type parameters, so Scala can't apply the conversion to Traverse.Ops
to use traverse syntax methods on objects defined with type Either
.
To make them available, you can define a type alias for Either
, that fixes the value of the first type parameter, and thus has only one type parameter. Scala then will be able to use the traverse syntax on the variables defined with this type alias:
type StringOr[T] = Either[String, T]
val e: StringOr[IO[Int]] = Right(IO(2))
e.sequence
Another method is to get an instance of Traverse
for your type, using type lambdas or the kind projector compiler plugin, and then call sequence
method on it passing your value:
val e: Either[String, IO[Int]] = Right(IO(2))
// With type lambda
Traverse[({ type L[T] = Either[String, T] })#L].sequence(e)
// With kind projector
Traverse[Either[String, ?]].sequence(e)
回答2:
In addition to Kolmar's answer (which is quite thorough), I'd like to propose an alternative and much easier solution.
There is a compiler flag since Scala 2.11.9 that allows it to recognize when types with multiple type parameters should behave like ones with only one. We call this "partial unification".
The easiest way to enable partial unification, is to add the sbt-partial-unification
plugin.
If you're on Scala 2.11.9 or newer, you can also simply add the compiler flag:
scalacOptions += "-Ypartial-unification"
Then your code compiles without a problem:
import cats.effect.IO
import cats.instances.either._
import cats.syntax.TraverseSyntax
object Test extends App with TraverseSyntax {
val e: Either[String, IO[Int]] = Right(IO(2))
e.sequence // No more error here
}
In the recently released Scala 2.13, it's now on by default, so it should just work out of the box there.
来源:https://stackoverflow.com/questions/52358347/traversing-either-in-scala