【问题标题】:Why does Some(x).map(_ => null) not evaluate to None?为什么 Some(x).map(_ => null) 不评估为无?
【发布时间】:2016-04-10 06:43:23
【问题描述】:

我最近在 Scala 中遇到了一个令人困惑的问题。我希望下面的代码会导致None,但它会导致Some(null)

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

这背后的原因是什么?为什么不产生None

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

【问题讨论】:

  • @soon,这不是那个问题的重复。该问题明确要求 Some(null)。我已经检查过了。谢谢。
  • 是的,不是完全重复,但它涵盖了与您的问题密切相关的 Some(null) 行为,因为它在 map 方法的引擎盖下使用。我还是撤回了投票
  • Some 的实例不是 None,顾名思义。这就是为什么Some(null)something 持有null,而不是“无”。

标签: scala optional


【解决方案1】:

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

Some 上的映射始终计算为Some。这是一个简单而有用的法则。如果我们做出你提议的改变,我们将不再拥有该法律。例如,这是我们可以肯定地说的一件事。对于所有fxy

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

如果我们要进行您提议的更改,则该声明将不再正确;具体来说,它不适用于f(x) == null

此外,Optionfunctor。 Functor 是对具有map 函数的事物的有用概括,它的定律与关于映射应该如何工作的直觉很好地对应。如果我们要进行您提议的更改,Option 将不再是函子。

null 是 Scala 中的一个异常,其存在仅用于与 Java 库的互操作性。放弃Option 作为函子的有效性并不是一个很好的理由。

【讨论】:

【解决方案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) 实际上会创建一个包含nullSome 对象。您可能想要做的是使用Option.apply,如果值为null,它会返回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))

当然,这段代码没有意义,但我会认为你只是在做某种实验。

【讨论】:

  • 谢谢,考虑到您为Option.mapSome 提供的源代码,我可以理解为什么会这样。我会接受你的回答。但是,除此之外,这不是令人困惑吗?在这种情况下,如果它只返回 None 不是更有意义吗?
  • 可能不是Option("a").map(s => Option.apply(null)),而是Option("a").flatMap(s => Option.apply(null))(甚至Option("a").flatMap(s => Option(null)))?
  • 您的最后一条语句的计算结果为 Some(None)。类型为Option[Option[Nothing]]。正如其他问题指出的那样,您应该改用flatmap
  • 已更正。谢谢@maasg。
【解决方案3】:

Optionnull 的替代品,但一般来说,当您与一些java 代码交谈时,您会在scala 中看到null,它不像Option 应该尽可能处理nulls , 它不是设计用于nulls 而是代替它们。然而,有一种方便的方法Option.apply 类似于java 的Optional.ofNullable 可以处理null 的情况,这主要是关于scala 中的nullsOptions。在所有其他情况下,它适用于SomeNone,无论是否在内部都没有任何区别。

如果您有一些来自 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 中并将其用作 Option。不要指望null 魔法会在你需要的时候发生。

【讨论】:

    【解决方案4】:

    要了解发生了什么,我们可以使用函数替换原理逐步探索给定的表达式:

    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
    

    【讨论】:

      【解决方案5】:

      为什么会是None,map 的签名是从值AB 产生Option[B] 的函数。在该签名中没有任何地方表明B 可能是null,因为说B 是一个选项[B]。 flatMap 但是确实表明返回的值也是可选的。它的签名是Option[A] => (A => Option[B]) => Option[B]

      【讨论】:

      • 所以在这种情况下,我希望它返回Option(null),这反过来又会返回None。这正是我困惑的原因。
      猜你喜欢
      • 2011-08-13
      • 1970-01-01
      • 1970-01-01
      • 2012-04-22
      • 2021-12-26
      • 2018-10-15
      • 1970-01-01
      • 2014-05-19
      • 2019-05-24
      相关资源
      最近更新 更多