Loom
Loom

Reputation: 9986

for-comprehension yield raises type mismatch compiler error

I want to extract from Iterable[Try[Int]] a list of all valid values (Iterable[Int])

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

The following is the way to print all valid values from test:

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

// Output
// 8
// 42

However, when I tried to save the valid values to list I have received an error:

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

How to fix the error and why it was raised?

Upvotes: 1

Views: 286

Answers (2)

proximator
proximator

Reputation: 688

You can also try:

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

or

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

Upvotes: 3

Mario Galic
Mario Galic

Reputation: 48420

Try collect

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

In a for-comprehension format that corresponds to

for { Success(p) <- test } yield p

Both make use of Constructor Patterns which under the hood perform isInstanceOf type test followed by asInstanceOf type cast. Verbosly that corresponds to something like

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

The following for-comprehension does not work because the monads in it have to align

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

The above for-comprehension desugars to

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

and looking at the signature of flatMap we see it expects a function

Try[Int] => IterableOnce[Int]

whilst we provide

Try[Int] => Try[Int]

because n.map((p: Int) => p) returns Try[Int]. Now the following for-comprehension is a whole different beast

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

because of the absence of yield it desugars to

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

where foreach expects a function of type

Try[Int] => Unit

which we indeed provide because n.foreach((p: Int) => println(p)) indeed returns Unit.

Upvotes: 5

Related Questions