0

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?

jwvh
  • 50,871
  • 7
  • 38
  • 64
St.Antario
  • 26,175
  • 41
  • 130
  • 318
  • i have not seen `Either` with traverse be it cats or scalaz. what you trying to achieve actually? I doubt you are looking for `.map` not `.traverse` – prayagupa Sep 16 '18 at 22:28
  • @prayagupd I want to perform transformation `Either[String, F[Int]]` --> `F[Either[String, Int]]` – St.Antario Sep 16 '18 at 22:32

2 Answers2

5

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.

Luka Jacobowitz
  • 22,795
  • 5
  • 39
  • 57
3

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)
Kolmar
  • 14,086
  • 1
  • 22
  • 25