在 Scala 中遍历 Either

Traversing Either in Scala

我写了下面的简单代码:

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
}

不幸的是,它拒绝使用

进行编译
Error:(25, 94) value sequence is not a member of scala.util.Either

你能解释一下为什么吗?我导入了 either 个实例,其中包括 Traverse[Either[A, ?]]。怎么了?

Traverse[F] 被定义为具有一个类型参数 F[T] 的类型的类型类。 Either 类型有两个类型参数,因此 Scala 无法将转换应用到 Traverse.Ops 以对使用类型 Either.

定义的对象使用遍历语法方法

为了使它们可用,您可以为 Either 定义一个类型别名,它固定第一个类型参数的值,因此只有一个类型参数。然后 Scala 将能够对使用此类型别名定义的变量使用遍历语法:

type StringOr[T] = Either[String, T]
val e: StringOr[IO[Int]] = Right(IO(2))
e.sequence

另一种方法是使用类型 lambda 或 kind projector compiler plugin 为您的类型获取 Traverse 的实例,然后在其上调用 sequence 方法传递您的值:

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 的回答(非常详尽)之外,我还想提出一个更简单的替代解决方案。

自 Scala 2.11.9 以来有一个编译器标志,允许它识别何时具有多个类型参数的类型应该表现得像只有一个类型参数的类型。 我们称之为 "partial unification".

启用部分统一的最简单方法是添加 sbt-partial-unification plugin.

如果您使用的是 Scala 2.11.9 或更新版本,您还可以简单地添加编译器标志:

scalacOptions += "-Ypartial-unification"

然后你的代码编译没有问题:

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
} 

在最近发布的 Scala 2.13 中,它现在默认启用,因此它应该开箱即用。