在Scala中将Option转换为Either

cld4siwp  于 6个月前  发布在  Scala
关注(0)|答案(4)|浏览(67)

假设我需要在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

svujldwt

svujldwt1#

不,如果你这样做,你不能离开的类型。
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")

7gs2gvoe

7gs2gvoe2#

我不确定你当时使用的是哪个版本的Scala。目前,使用Scala 2.12.6,你的代码没有像这样的编译问题:

def foo(ox: Option[Int]): Either[String, Int] =
  ox.toRight("No number")

字符串
我想说的另一点是,折叠(虽然折叠是我的首选方法,它可以折叠几乎所有具有fold方法的东西)通常需要类型参数的帮助。编译器可以通过两种方式对表达式进行类型检查,要么推断类型参数,要么简单地找到显式定义的类型参数。
在你的例子中,如果你试图像这样折叠一个选项:

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)

gblwokeq

gblwokeq3#

类型注解之所以是必要的,是因为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)将improvements引入类型推断,因此您的代码片段可以开箱即用,而无需提供显式的类型注解

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]

6fe3ivhb

6fe3ivhb4#

最简单的方法是使用Option.toRight

val ox: Option[Int] = ...
  ox.toRight("No number")

字符串

相关问题