在Scala中遍历

时间:2018-09-16 21:02:12

标签: scala functional-programming scala-cats either

我编写了以下简单代码:

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的{​​{1}}实例。怎么了?

2 个答案:

答案 0 :(得分:5)

除了Kolmar的答案(非常详尽)之外,我还想提出一个替代且更容易的解决方案。

从Scala 2.11.9开始,存在一个编译器标志,该标志允许它识别带有多个类型参数的类型何时应表现为仅具有一个类型参数的类型。 我们称之为“部分统一”。

启用部分统一的最简单方法是添加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
} 

答案 1 :(得分:3)

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

另一种方法是使用lambdas或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)