1

来自 Oderskey 和 Venners 的规范“Scala 编程”:

scala> val results = List(Some("apple"), None,
                   Some("orange"))
        results: List[Option[java.lang.String]] = List(Some(apple),
        None, Some(orange))
        scala> for (Some(fruit) <- results) println(fruit)
        apple
        orange

我不明白 scala 的哲学强加给程序员明确提及 Some(apple) 而不是推断它的需要。我更愿意写/看以下内容:

scala> val results = List("apple", None, "orange")
        results: List[Option[java.lang.String]] = List(apple, None, orange)
        scala> for (fruit <- results) println(fruit)
        apple
        orange

或者至少是以下(提供在列表级别而不是在单个项目级别输入):

scala> val results :List[Option[String]] = ("apple", None, "orange")
        results: List[Option[java.lang.String]] = List(apple, None, orange)
        scala> for (fruit <- results) println(fruit)
        apple
        orange

至少在最后一种情况下:提供了 List 的类型(以帮助编译器..),但我们仍然避免像 Some('foo') 那样将 List 中的每个元素“装箱”的冗长。

任何更符合 scala 思维方式的人都可以告诉我为什么我必须做额外的打字..?

编辑:所以以下是我想要的字符串。

scala> implicit def toOpt(a : String) = Some(a)
toOpt: (a: String)Some[String]

scala> val  myList : List[Option[String]] = List("first", None, "third")
myList: List[Option[String]] = List(Some(first), None, Some(third))

如果有人可以展示如何使用更高种类的类型来概括上述内容,我将奖励答案。

4

5 回答 5

4

除了 Ankur 的回答,Option是 Scala 中的 Monad,意味着它完全实现了map, flatMap.

这样你就可以使用它for comprehensions

   def sum(a: Some[Int], b: Some[Int]): Int = {
      for {
        a <- maybeA
        b <- maybeB
       ...
      } yield {
        a + b
      }
    } getOrElse 0 

或者map超过它的内容:val a = Some(5); a map (3 + ) // gives you Some(8)

monadic 方法的类型签名很复杂,编译器难以处理。

Optional[A]和之间的身份更加模糊A是无用的,根本不可能。

冗长

你的说法没有充分的根据。在实践中你会发现 scala.Option 非常强大。

假设你有:

def methodA: Option[ComplexType]
def methodB: Option[MoreComplexType]
def methodC: Option[InsanelyComplexStructure]

然而,要链接并检查是否存在,您所需要的只是理解:

for {
   a <- methodA
   b <- methodB
   c <- methodC
} yield {
   // now this gets executed only if all the 3 methods return Some()
}

这变得非常强大。深度嵌套的 if 语句是一个糟糕的旧记忆。

于 2013-10-15T11:28:44.723 回答
4

不要认为我很粗鲁,但答案是因为编译器不是想猜测你的愿望的魔术师

如果有样板(类型推断,隐式转换),它将为您提供帮助,但编译器的全部意义(至少对于具有类型系统的语言)是为了捕捉模棱两可的情况,例如

val results :List[Option[String]] = ("apple", None, "orange")

对你大喊大叫嘿伙计,你一定是做错了什么!. 您基本上是在问为什么编译器不允许我将狗分配给猫,将橙色分配给苹果或技巧分类游戏,当语言允许此类技巧时,会发生一些非常糟糕和讨厌的事情

for (fruit <- results) println(fruit)

语言应该如何知道您要打印底层的未装箱值而不是选项本身?如果您使用其他功能,它应该怎么做?如果我有Option[Option[String]]怎么办?你看,有很多事情需要考虑,如果我们真的认为在这种特殊情况下额外的打字是一个真正的问题,我们必须坐下来在大书中列举所有这些情况,每个人都必须检查它以了解每一个可能的结果,否则代码是不可预测的。还要注意,会有隐藏的性能下降:例如编译器会隐式地将一个集合转换为另一个集合,它们毕竟是不同的类和不同的结构并且令人惊讶——你不知道在哪里。至少不需要额外查看您的代码。毕竟,你为什么不期待

val i: List[Int] = 3

有效吗?

于 2013-10-15T13:19:41.297 回答
3

因为这会导致一些严重的歧义。

检查以下代码段(基于您的建议示例)。

List("apple", "hello", "orange")

上面的代码List[Option[String]]或者List[String],两者都是有效类型,选择哪一个?

更新:

让我们再举一个案例类的例子(Option是一个有 2 个案例的案例类SomeNone):

abstract class Superhero
case class Superman(health : Int) extends Superhero
case class Spiderman(health: Int) extends Superhero

现在我们面临另一个模棱两可的问题,如果我们说: List[Superhero](100,100), 是前 100 是超人还是蜘蛛侠,与下一个 100 相同。

于 2013-10-15T11:20:34.353 回答
2

我不认可这一点,但您似乎正在寻找的是:

scala> implicit def aToOptionA[T](a:T):Option[T]=Some(a)
warning: there were 1 feature warning(s); re-run with -feature for details
aToOptionA: [T](a: T)Option[T]

scala> val  myList : List[Option[String]] = List("first", None, "third")
myList: List[Option[String]] = List(Some(first), None, Some(third))

scala> val  myList2:List[Option[Int]]  = List(1, None, 2)
myList2: List[Option[Int]] = List(Some(1), None, Some(2))
于 2013-10-15T20:32:36.183 回答
0

为了在有限的代码部分中使用,您可以使用隐式:

implicit def string2SomeString(s: String): Option[String] = Some(s)

val l: List[Option[String]] = List("a", None, "b")

但是,当出现歧义时,您可能会遇到困难。

更一般的隐式是可行的,但可能会导致更多的歧义:

implicit def a2SomeA[A](s: A): Option[A] = Some(s)
于 2013-10-15T15:15:46.653 回答