Scala中Option的for推导式解释

5

我有以下定义:

def f: Option[String] = Some(null)

以下内容的求值结果为None:
for {x:String <- f} yield {
  x
}

以下表达式的求值结果为 Some(null):
for {x <- f} yield {
  x
}

以下表达式的结果是Some(null):
f.map((x:String) => x)

我想知道它们之间的差异是什么?

2个回答

5
解糖处理发生在解析器中,因此-Xprint:parser显示了差异:
$ scala -Xprint:parser
Welcome to Scala 2.12.1 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_111).
Type in expressions for evaluation. Or try :help.

scala> for (s: String <- (Some(null): Option[String])) yield s
[[syntax trees at end of                    parser]] // <console>
package $line3 {
  object $read extends scala.AnyRef {
    def <init>() = {
      super.<init>();
      ()
    };
    object $iw extends scala.AnyRef {
      def <init>() = {
        super.<init>();
        ()
      };
      object $iw extends scala.AnyRef {
        def <init>() = {
          super.<init>();
          ()
        };
        val res0 = (Some(null): Option[String]).withFilter(((check$ifrefutable$1) => check$ifrefutable$1: @scala.unchecked match {
  case (s @ (_: String)) => true
  case _ => false
})).map(((s: String) => s))
      }
    }
  }
}

res0: Option[String] = None

这让我感到惊讶,因为我认为以这种方式进行过滤是人们想要的功能,但尚未实现。

类型模式只是一个instanceof测试,所以null无法通过该测试。

没有过滤器:

scala> for (s <- (Some(null): Option[String])) yield s
[[syntax trees at end of                    parser]] // <console>
package $line4 {
  object $read extends scala.AnyRef {
    def <init>() = {
      super.<init>();
      ()
    };
    object $iw extends scala.AnyRef {
      def <init>() = {
        super.<init>();
        ()
      };
      object $iw extends scala.AnyRef {
        def <init>() = {
          super.<init>();
          ()
        };
        val res1 = (Some(null): Option[String]).map(((s) => s))
      }
    }
  }
}

res1: Option[String] = Some(null)

在2.9版本中:
$ scala29
Welcome to Scala version 2.9.3 (OpenJDK 64-Bit Server VM, Java 1.6.0_38).
Type in expressions to have them evaluated.
Type :help for more information.

scala> for (s: String <- (Some(null): Option[String])) yield s
res0: Option[String] = Some(null)

因此,在2.10.x版本中添加了筛选功能。

编辑:实际上,这个是你没有理解的内容:

scala> for (s: String <- (Some("x"): Option[Any])) yield s
<console>:12: error: type mismatch;
 found   : String => String
 required: Any => ?
       for (s: String <- (Some("x"): Option[Any])) yield s
                      ^

https://dev59.com/r1TTa4cB1Zd3GeqPqC9a - som-snytt

1

好的...首先要说的是,“在Scala世界里,尽可能远离null怪兽”。它们很危险。

现在...为了理解这种行为,在Scala shell中尝试的第一件事是以下内容:

scala> val n = null
n: Null = null

那么,在Scala中,nullNull类的一个实例。

现在,让我们看看当我们将这个nullOption混合时会发生什么。

scala> val nullOpt1 = Option(null)
nullOpt1: Option[Null] = None

scala> val nullOpt2 = Some(null)
nullOpt2: Some[Null] = Some(null)

请注意...两者之间的差异...因此,Scala的人们认为可能有人想将null包装成Option...所以他们允许他们明确地使用Some.applynull包装成Option。而Option.apply的使用行为更加"智能",当您尝试包装null时,它会给您一个None

现在...让我们先看看如何为Option实现map

final def map[B](f: A => B): Option[B] =
  if (isEmpty) None else Some(f(this.get))

现在应该很清楚为什么 "Some(null).map((x: String) => x)" 会给你 "Some(null)" 了吧?
至于理解 "for" 的情况,这需要对如何使用 "Repr" 实现 "Option" 的 "for" 有一定的了解。然后行为就会变得清晰明了。

@som-snytt 是的,这正是重点。因此...无论Some中有什么“值”(即使为空),this.get都将返回该“值”,在本例中,f只是x => x,因此结果为Some(null) - sarveshseri
我能理解为什么 Some(null).map((x: String) => x) 会返回 Some(null),但我不明白为什么 {x:String <- f} yield x 却返回 None,因为从理论上讲它们应该是相同的。 - Chi Zhang

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接