Scala Option.fold 在函数内外表现不同

Scala Option.fold Behaves differently in and out of function

取简单函数:

def makeUpper(input: Option[String]): String = 
    input.fold("b"){ _.toUpperCase }

makeUpper(Some("a"))   // A
makeUpper(None)        // b

这符合预期。

现在,相同的代码,但在函数之外:

Some("a").fold("b"){ _.toUpperCase }   // A
None.fold("b"){ _.toUpperCase }        // error: value toUpperCase is not a member of Nothing

注:

Option.empty[String].fold("b"){ _.toUpperCase }   // b

问题:

  1. 为什么会有不同的行为?为什么会出现函数外的错误?

函数是否将输入 None 转换为“确定的”Option.empty[String]?

  1. 从函数 return 选项(某些或 None)处理 return 的正确方法是什么...我们是否总是必须处理 return 另一个函数中的值以避免上述问题?

我错过了什么?

None 定义为:

case object None extends Option[Nothing]

因此,当您对 None 的“内容”调用方法时,您是在对类型 Nothing 的值调用方法。这种类型没有方法 toUpperCase 所以你得到一个错误。

然而 Nothing 与所有类型兼容,并且 Option 与其类型参数是协变的,因此 Option[Nothing]Option[String] 兼容。这就是为什么可以将 None 传递给您的 makeUpper 方法。一旦已知内容的类型是 String,就可以对其调用 toUpperCase 方法。

Scala 编译器可以推断类型。

因为你已经将函数参数input定义为Option[String],即使你传递None,编译器也知道它是一个Option[String]并适当地类型化它.

这是在做什么,

val none: Option[String] = None
none.fold("b"){ _.toUpperCase }

或者这个,

(None: Option[String]).fold("b"){ _.toUpperCase }

在没有任何额外信息的情况下直接使用 None 时,编译器仍会尝试仅使用可用信息 None extends Option[Nothing] 来推断最佳猜测,因此将其视为 Option[Nothing]。这相当于,

val none: Option[Nothing] = None
none.fold("b"){ _.toUpperCase }

或者这个,

(None: Option[Nothing]).fold("b"){ _.toUpperCase }

请注意,在这种情况下,没有迹象表明与 String 有任何关系。

只要您通过在一个地方告诉您的预期类型来帮助编译器。它将能够推断其他相关位置的预期类型。

scala> val noneIntOpt = noneStringOpt
// val noneIntOpt: Option[String] = None

scala> val noneIntOpt = noneStringOpt.map(_.length)
// val noneIntOpt: Option[Int] = None