为什么两个 Some[Int] 可以在 for/yield 表达式中求和?

Why can two Some[Int] sum up in for/yield expression?

val listSomeInt: List[Some[Int]] = List(Some(1),Some(2))
// Some(1) + Some(2) will not compile of course

问题一:为什么同样的和计算在for/yield表达式中有效?

val sumSomeInt = for{
    i1<- listSomeInt(0)
    i2<- listSomeInt(1)
} yield i1 + i2
println(sumSomeInt) // Some(3)

问题 2:如何通过 println(i1.getClass) 或其他方式查看 "for{}" 中 i1 的类型?
反编译可能会给出一些提示,但不是那么彻底(对我而言)。

private final List<Some<Object>> listSomeInt = …;
public List<Some<Object>> listSomeInt() { return this.listSomeInt; }
public Option<Object> sumSomeInt() { return this.sumSomeInt;  }
private final Option<Object> sumSomeInt = (Option)listSomeInt().apply(0)).flatMap(this::$anonfun$sumSomeInt$adapted);

这个:

for{
    i1<- listSomeInt(0)
    i2<- listSomeInt(1)
} yield i1 + i2

是语法糖:

listSomeInt(0).flatMap { i1 =>
  listSomeInt(1).map { i2 =>
    i1 + i2
  }
}

等于:

Option(1).flatMap { i1 =>
  Option(2).map { i2 =>
    i1 + i2
  }
}

您可以进一步解释为:

Option(1).map { i1: Int => // i1 == 1 in this example
  Option(2).map { i2: Int => // i2 == 2 in this example
    i1 + i2 // i1 + i2 == 3 in this example
  } // Option[Int] == Some(3)
} // Option[Option[Int]] == Some(Some(3))
.flatten // Option[Int] == Some(3)

如果您使用像菊石这样的 REPL 来测试部分结果,您可以预览其中的许多内容:

@ Option(1).map(i => i *2)
res9: Option[Int] = Some(2)

@ Option(1).map(i => Option(i * 2))
res10: Option[Option[Int]] = Some(Some(2))

@ Option(1).map(i => Option(i * 2)).flatten
res11: Option[Int] = Some(2)

@ Option(1).flatMap(i => Option(i * 2))
res12: Option[Int] = Some(2)

@ Option(1).map(i => Option(2).map(j => i + j)).flatten
res13: Option[Int] = Some(3)

@ Option(1).flatMap(i => Option(2).map(j => i + j))
res14: Option[Int] = Some(3)

很好 IDE(例如 IntelliJ)也能够将类型添加到代码中。

for-comprehension只是flatMap、map、filter的语法糖,你的代码相当于:

 val option1 = Some(1)
 val option2 = Some(2)
 val sumOptions = option1.flatMap { i =>
      option2.map { j =>
        i + j
      }
    }