我最近在Scala遇到了一个令人困惑的问题。我希望以下代码生成None
,但会产生Some(null)
:
Option("a").map(_ => null)
这背后的原因是什么?为什么它不会导致None
?
注意:此问题与Why Some(null) isn't considered None?不重复,因为问题要求明确使用Some(null)
。我的问题是关于使用Option.map
。
答案 0 :(得分:8)
每次我们为规则添加例外时,我们都会剥夺自己推理代码的工具。
Some
上的映射始终评估为Some
。这是一个简单而有用的法律。如果我们要提出你提出的改变,我们将不再拥有该法律。例如,我们可以肯定地说出这一点。适用于所有f
,x
和y
:
Some(x).map(f).map(_ => y) == Some(y)
如果我们要提出你提出的改变,那么这种说法将不再适用;具体而言,它不适用于f(x) == null
。
此外,Option
是functor。 Functor是对具有map
函数的事物的有用泛化,并且它具有与关于映射应如何工作的直觉很好地对应的规律。如果我们要进行您提出的更改,Option
将不再是一个仿函数。
null
是Scala中的一种异常现象,仅用于与Java库的互操作性。将Option
的有效性作为仿函数放弃并不是一个好理由。
答案 1 :(得分:2)
以下是code for Option map
method:
/** Returns a $some containing the result of applying $f to this $option's
* value if this $option is nonempty.
* Otherwise return $none.
*
* @note This is similar to `flatMap` except here,
* $f does not need to wrap its result in an $option.
*
* @param f the function to apply
* @see flatMap
* @see foreach
*/
@inline final def map[B](f: A => B): Option[B] =
if (isEmpty) None else Some(f(this.get))
因此,正如您所看到的,如果该选项不为空,它将使用函数返回的值映射到Some
。这是code for Some
class:
/** Class `Some[A]` represents existing values of type
* `A`.
*
* @author Martin Odersky
* @version 1.0, 16/07/2003
*/
@SerialVersionUID(1234815782226070388L) // value computed by serialver for 2.11.2, annotation added in 2.11.4
final case class Some[+A](x: A) extends Option[A] {
def isEmpty = false
def get = x
}
因此,正如您所看到的,Some(null)
实际上会创建一个包含Some
的{{1}}对象。您可能想要做的是使用null
,如果值为Option.apply
,则会返回None
。这是code for Option.apply
method:
null
所以,你需要编写这样的代码:
/** An Option factory which creates Some(x) if the argument is not null,
* and None if it is null.
*
* @param x the value
* @return Some(value) if value != null, None if value == null
*/
def apply[A](x: A): Option[A] = if (x == null) None else Some(x)
当然,这段代码毫无意义,但我会认为你只是在进行某种实验。
答案 2 :(得分:2)
Option
可以替代null
,但一般情况下,当您在谈论某些Java代码时,您会在scala中看到null
,它与Option
不一样应尽可能处理nulls
,它不是设计用于nulls
而是用于它们。然而,常规方法Option.apply
类似于处理Optional.ofNullable
案例的java null
,而且大部分都是关于nulls
和scala中的Options
。在所有其他情况下,它适用于Some
和None
,如果null在内部或不在内,则没有任何区别。
如果你有一些讨厌的方法返回来自java的null
而你想直接使用它,请使用以下方法:
def nastyMethod(s: String): String = null
Some("a").flatMap(s => Option(nastyMethod(s)))
// or
Some("a").map(nastyMethod).flatMap(Option(_))
输出Option[String] = None
因此,nastyMethod
可以返回String
或null
概念上是Option
,因此将其结果包装在Option
中并将其用作{ {1}}。不要期望Option
魔法会在你需要的时候发生。
答案 3 :(得分:2)
为了理解发生了什么,我们可以使用功能替代原则逐步探索给定的表达式:
Option("a").map(s => null) // through Option.apply
Some("a").map(s => null) // let's name the anonymous function as: f(x) = null
Some("a").map(x => f(x)) // following Option[A].map(f:A=>B) => Option[B]
Some(f("a")) // apply f(x)
Some(null)
问题中表达的混淆来自假设map
将适用于Option
之前 Option.apply
被评估的论点:让我们看看这怎么可能不起作用:
Option("a").map(x=> f(x)) // !!! can't evaluate map before Option.apply. This is the key to understand !
Option(f(a)) // !!! we can't get here
Option(null) // !!! we can't get here
None // !!! we can't get here
答案 4 :(得分:0)
为什么它是([A-Za-z])\1+
,地图的签名是从值None
到A
的函数,以产生B
。请勿在该签名中指明Option[B]
可能是B
,其中B是选项[B]。但是,null
表示返回的值也是可选的。它的签名是flatMap
。