为什么 Some(x).map(_ => null) 不计算为 None?
Why does Some(x).map(_ => null) not evaluate to None?
我最近在 Scala 中遇到了一个令人困惑的问题。我希望以下代码会导致 None
,但会导致 Some(null)
:
Option("a").map(_ => null)
这背后的原因是什么?为什么它不会导致 None
?
注意:此问题不是 Why Some(null) isn't considered None? 的重复问题,因为该问题要求明确使用 Some(null)
。我的问题是关于使用 Option.map
.
这里是 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)
实际上会创建一个包含 null
的 Some
对象。您可能想要做的是使用 Option.apply
,如果值为 null
,它会执行 returns 和 None
。这是 code for Option.apply
method:
/** 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)
因此,您需要这样编写代码:
Option("a").flatMap(s => Option.apply(null))
当然,这段代码没有任何意义,但我会认为你只是在做某种实验。
为什么会是None
,map的签名是一个从值A
到B
产生Option[B]
的函数。在那个签名中没有任何地方通过说 B 是一个选项 [B] 来表明 B
可能是 null
。 flatMap
但是确实表明返回的值也是可选的。它的签名是 Option[A] => (A => Option[B]) => Option[B]
.
Option
是 null
的一种替代品,但通常当您与某些 java 代码交谈时,您会在 scala 中看到 null
,它不像Option
应该尽可能处理 nulls
,它不是设计用来与 nulls
一起使用的,而是用来代替它们的。然而,有一种方便的方法 Option.apply
类似于 java 的 Optional.ofNullable
可以处理 null
的情况,而这主要是关于 nulls
和 Options
在 Scala 中。在所有其他情况下,它在 Some
和 None
上工作,无论 null 是否在内部都没有任何区别。
如果您有一些来自 java 的令人讨厌的方法 returning 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
可以 return 一个 String
或 null
在概念上是一个 Option
,因此将其结果包装在一个 Option
中并将其用作 Option
。不要指望 null
魔法会在您需要的时候出现。
每次我们向规则添加例外时,我们就剥夺了自己推理代码的工具。
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
作为仿函数的有效性不是一个好的理由。
为了理解这是怎么回事,我们可以使用函数替换原理来一步步探索给定的表达式:
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
我最近在 Scala 中遇到了一个令人困惑的问题。我希望以下代码会导致 None
,但会导致 Some(null)
:
Option("a").map(_ => null)
这背后的原因是什么?为什么它不会导致 None
?
注意:此问题不是 Why Some(null) isn't considered None? 的重复问题,因为该问题要求明确使用 Some(null)
。我的问题是关于使用 Option.map
.
这里是 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)
实际上会创建一个包含 null
的 Some
对象。您可能想要做的是使用 Option.apply
,如果值为 null
,它会执行 returns 和 None
。这是 code for Option.apply
method:
/** 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)
因此,您需要这样编写代码:
Option("a").flatMap(s => Option.apply(null))
当然,这段代码没有任何意义,但我会认为你只是在做某种实验。
为什么会是None
,map的签名是一个从值A
到B
产生Option[B]
的函数。在那个签名中没有任何地方通过说 B 是一个选项 [B] 来表明 B
可能是 null
。 flatMap
但是确实表明返回的值也是可选的。它的签名是 Option[A] => (A => Option[B]) => Option[B]
.
Option
是 null
的一种替代品,但通常当您与某些 java 代码交谈时,您会在 scala 中看到 null
,它不像Option
应该尽可能处理 nulls
,它不是设计用来与 nulls
一起使用的,而是用来代替它们的。然而,有一种方便的方法 Option.apply
类似于 java 的 Optional.ofNullable
可以处理 null
的情况,而这主要是关于 nulls
和 Options
在 Scala 中。在所有其他情况下,它在 Some
和 None
上工作,无论 null 是否在内部都没有任何区别。
如果您有一些来自 java 的令人讨厌的方法 returning 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
可以 return 一个 String
或 null
在概念上是一个 Option
,因此将其结果包装在一个 Option
中并将其用作 Option
。不要指望 null
魔法会在您需要的时候出现。
每次我们向规则添加例外时,我们就剥夺了自己推理代码的工具。
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
作为仿函数的有效性不是一个好的理由。
为了理解这是怎么回事,我们可以使用函数替换原理来一步步探索给定的表达式:
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