maow
maow

Reputation: 1407

Type mis-match after flattening a Some(Option) in fold function

I want to get the product of a List[Option[Int]], and flatten it to Option[Int]:

List(Some(2), Some(3), Some(5)).foldLeft(Some(1)) { (x, y) =>

  // err
  x.map(ix => y.map(iy => ix * iy)).flatten

  //   // workaround
  //   x.map(ix => y.map(iy => ix * iy)).flatten match {
  //    case Some(acc) => Some(acc)
  //     case _ => Some(0)
  //   }

}

the err in detail:

polymorphic expression cannot be instantiated to expected type;
[error]  found   : [B]Option[B]
[error]  required: Some[Int]
[error]       x.map(ix => y.map(iy => ix * iy)).flatten 
[error]  

flatten in foldLeft didn't return Some[Int]? What's going on?

Upvotes: 0

Views: 197

Answers (3)

mohit
mohit

Reputation: 4999

x.map(ix => y.map(iy => ix * iy)).flatten

returns an Option[Int], based on the value of the x and y, i.e. if either x or y is None the value is None, otherwise it is Some[Int].

In your fold statement, your seed value is Some(1) whose type is Some[Int]. Compiler expects the same type from the result of the fold statement. But the result value of the expression of the fold statement is Option[Int]. Hence, the error, the required type is Some[Int] as of the seed value, but compiler found Option[Int] from the fold expression.

Any of the other answer works. The closet to your expression will be simply change your seed value to Option(1) from Some(1), so that the type of seed value and type from the fold statement matches, like

List(Some(2), Some(3), Some(5)).foldLeft(Option(1)) { (x, y) => ....

Upvotes: 2

Waldemar Wosiński
Waldemar Wosiński

Reputation: 1580

This works and is simpler:

List(Some(2), Some(3), Some(5)).flatten.foldLeft(1) { (x, y) => x * y}

Flattten removes Nones and gives you List[Int].

Upvotes: 0

goralph
goralph

Reputation: 1086

If your list is actually a List[Option[Int]] then for a working example you need to declare them as such. It would be great if someone could chime in on exactly why since I'm not sure.

val xs = List(Option(2), Option(3), Option(5))

xs.reduceRight((x, b) => x.flatMap(y => b.map(z => y * z)))
// res0: Option[Int] = Some(30)

Note however if any element in the list of Options is None then the evaluation will "short circuit" and return a None. If you don't want this then I believe you will need to look at some solutions from scalaz such as applicative functors, there's some discussion this here Summing a List of Options with Applicative Functors

Upvotes: 1

Related Questions