for-comprehension yield 引发类型不匹配编译器错误

for-comprehension yield raises type mismatch compiler error

我想从 Iterable[Try[Int]] 中提取所有有效值的列表 (Iterable[Int])

val test = List(
    Try(8), 
    Try(throw new RuntimeException("foo")), 
    Try(42), 
    Try(throw new RuntimeException("bar"))
)

以下是从test打印所有有效值的方法:

for {
    n <- test
    p <- n
} println(p)

// Output
// 8
// 42

但是,当我尝试将有效值保存到列表时,我收到了一个错误:

val nums: Seq[Int] = for {
    n <- list
    p <- n    // Type mismatch. Required: IterableOnce[Int], found Try[Int]
} yield(p)
println(nums)

如何修复错误以及出现错误的原因?

尝试收集

test.collect { case Success(value) => value }
// res0: List[Int] = List(8, 42)

在对应于

的理解格式中
for { Success(p) <- test } yield p

两者都使用 Constructor Patterns,它在幕后执行 isInstanceOf 类型测试,然后是 asInstanceOf 类型转换。详细地对应于

test
  .filter (_.isInstanceOf[Success[Int]])
  .map    (_.asInstanceOf[Success[Int]].value)

下面的 for-comprehension 不起作用,因为其中的 monads 必须对齐

for {
  n <- test  // List monad
  p <- n     // does not align with Try monad
} yield (p)

以上理解脱糖为

test.flatMap((n: Try[Int]) => n.map((p: Int) => p))

并查看 flatMap 的签名,我们发现它需要一个函数

Try[Int] => IterableOnce[Int]

同时我们提供

Try[Int] => Try[Int]

因为n.map((p: Int) => p)returnsTry[Int]。现在下面的 for-comprehension 完全不同了[=​​30=]

for {
    n <- test
    p <- n
} println(p)

因为没有 yield 它脱糖为

test.foreach((n: Try[Int]) => n.foreach((p: Int) => println(p)))

其中 foreach 需要类型为

的函数
Try[Int] => Unit

我们确实提供了,因为 n.foreach((p: Int) => println(p)) 确实 returns Unit.

您也可以试试:

val nums: Seq[Int] = list.map(_.toOption).flatten

val nums: Seq[Int] = list.flatMap(_.toOption)