假设我需要在 Scala 中将
Option[Int]
转换为 Either[String, Int]
。我想这样做:
def foo(ox: Option[Int]): Either[String, Int] =
ox.fold(Left("No number")) {x => Right(x)}
不幸的是上面的代码无法编译,我需要显式添加类型
Either[String, Int]
:
ox.fold(Left("No number"): Either[String, Int]) { x => Right(x) }
是否可以通过这种方式将
Option
转换为Either
而不添加类型?Option
转换为 Either
?
不,如果你这样做,你就不能省略类型。
Left("No number")
的类型被推断为Either[String, Nothing]
。仅从 Left("No number")
来看,编译器无法知道您希望 Either
的第二种类型为 Int
,并且类型推断不会走得太远,以至于编译器将查看整个方法并决定它应该是Either[String, Int]
。
您可以通过多种不同的方式来做到这一点。例如模式匹配:
def foo(ox: Option[Int]): Either[String, Int] = ox match {
case Some(x) => Right(x)
case None => Left("No number")
}
或者使用
if
表达式:
def foo(ox: Option[Int]): Either[String, Int] =
if (ox.isDefined) Right(ox.get) else Left("No number")
或与
Either.cond
:
def foo(ox: Option[Int]): Either[String, Int] =
Either.cond(ox.isDefined, ox.get, "No number")
我不确定您当时使用的是哪个版本的 Scala。目前,使用 Scala 2.12.6,您的代码不存在这样的编译问题:
def foo(ox: Option[Int]): Either[String, Int] =
ox.toRight("No number")
我想说的另一点是,折叠(虽然这是我折叠任何具有折叠方法的东西的首选方法)通常需要类型参数的帮助。编译器可以通过两种方式对表达式进行类型检查,要么推断类型参数,要么简单地找到显式定义的类型参数。
在您的示例中,如果您尝试像这样折叠选项:
def foo(ox: Option[Int]): Either[String, Int] =
ox.fold(Left("No number") : Either[String, Int])(x => Right(x))
您显式提供有关第一个参数的类型信息,然后可以使用该信息来推断
fold
的类型参数。您正在帮助类型推断机制。
另一方面,您可以简单地显式提供
fold
的类型参数,如下所示:
def foo(ox: Option[Int]): Either[String, Int] =
ox.fold[Either[String, Int]](Left("No number"))(x => Right(x))
现在,您的实际(值级别)参数不会充斥着多余的类型信息,并且当编译器查看它时,不会进行类型推断,它可以立即告诉
fold
的类型参数是什么,就像它一样明确规定。使用方括号明确指定类型参数。
还有一点,关于
x => Right(x)
,您实际上是在创建一个新的函数文字,它除了将 x 传递到 Right case 类的伴生对象的 apply
方法之外什么也不做。您已经有了可用的适当形状的函数。它接受 x
并返回 Right(x)
。这就是apply
方法。可以直接参考(通过)。
def foo(ox: Option[Int]): Either[String, Int] =
ox.fold[Either[String, Int]](Left("No number"))(Right.apply)
类型注释之所以必要,是因为 Scala 2 中类型推断的工作方式:多个参数列表,其中
考虑
Option#fold
的签名
def fold[B](ifEmpty: => B)(f: A => B): B
我们可以看到类型参数
B
和两个参数列表。现在提供给第一个参数列表的参数类型是 Left[String,Nothing]
因为
scala> Left("No number")
val res0: scala.util.Left[String,Nothing] = Left(No number)
这意味着类型参数
B
被推断为Left[String,Nothing]
,这反过来又将提供给第二个参数列表的参数的预期类型限制为Left
返回类型的函数
A => Left[String,Nothing]
但是我们提供了
Right
返回类型的函数,因此会出现类型不匹配的错误
Welcome to Scala 2.13.3 (OpenJDK 64-Bit Server VM, Java 1.8.0_252).
Type in expressions for evaluation. Or try :help.
scala> def foo(ox: Option[Int]): Either[String, Int] =
| ox.fold(Left("No number")) {x => Right(x)}
ox.fold(Left("No number")) {x => Right(x)}
^
On line 2: error: type mismatch;
found : scala.util.Right[Nothing,Int]
required: scala.util.Left[String,Nothing]
请注意,Scala 3 (Dotty) 为类型推断带来了改进,因此您的代码片段可以开箱即用,无需提供显式类型注释
Starting dotty REPL...
scala> def foo(ox: Option[Int]): Either[String, Int] =
| ox.fold(Left("No number")) {x => Right(x)}
|
def foo(ox: Option[Int]): Either[String, Int]
最简单的方法是使用
Option.toRight
:
val ox: Option[Int] = ...
ox.toRight("No number")