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
问题:
- 为什么会有不同的行为?为什么会出现函数外的错误?
函数是否将输入 None 转换为“确定的”Option.empty[String]?
- 从函数 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
取简单函数:
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
问题:
- 为什么会有不同的行为?为什么会出现函数外的错误?
函数是否将输入 None 转换为“确定的”Option.empty[String]?
- 从函数 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