为什么Some(x).map(_ => null)不能评估为None?

时间:2016-04-10 06:43:23

标签: scala optional

我最近在Scala遇到了一个令人困惑的问题。我希望以下代码生成None,但会产生Some(null)

Option("a").map(_ => null)

这背后的原因是什么?为什么它不会导致None

注意:此问题与Why Some(null) isn't considered None?不重复,因为问题要求明确使用Some(null)。我的问题是关于使用Option.map

5 个答案:

答案 0 :(得分:8)

每次我们为规则添加例外时,我们都会剥夺自己推理代码的工具。

Some上的映射始终评估为Some。这是一个简单而有用的法律。如果我们要提出你提出的改变,我们将不再拥有该法律。例如,我们可以肯定地说出这一点。适用于所有fxy

Some(x).map(f).map(_ => y) == Some(y)

如果我们要提出你提出的改变,那么这种说法将不再适用;具体而言,它不适用于f(x) == null

的情况

此外,Optionfunctor。 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。在所有其他情况下,它适用于SomeNone,如果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可以返回Stringnull概念上是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+ ,地图的签名是从值NoneA的函数,以产生B。请勿在该签名中指明Option[B]可能是B,其中B是选项[B]。但是,null表示返回的值也是可选的。它的签名是flatMap