sc_ray
sc_ray

Reputation: 8043

Type mismatch with map and flatMap

While trying to play with Options in scala I have come across this peculiar problem.

I started off with creating a List[Option[Int]] as follows:

scala> List(Some(1),Some(2),None,Some(3))
res0: List[Option[Int]] = List(Some(1), Some(2), None, Some(3))

Then I tried to map an addition to 1 over the entries of the list in res0 as follows:

scala> res0 map (_ + 1)

This gave me the error:

<console>:9: error: type mismatch;
 found   : Int(1)
 required: String
              res0 map (_ + 1)
                            ^

Then I tried flatMapping an addition over the entries as follows:

scala> res0 flatMap (_ + 1)

This gave me the same error:

<console>:9: error: type mismatch;
 found   : Int(1)
 required: String
              res0 flatMap (_ + 1)
                                ^

But something like res0.flatMap(r => r) works just fine with a result of:

res9: List[Int] = List(1, 2, 3)

Can anybody tell me why adding the entry to 1 would fail for both map and flatMap?

Upvotes: 1

Views: 1634

Answers (6)

korefn
korefn

Reputation: 955

Try to use get to extract the value from Some[Int] to Int allowing for calculation value + 1 ie:

res0 map{_.getOrElse(0) + 1}

As pointed out by @Sepp2k you could alternatively use a collect to avoid having a default for None

res0 collect {case Some(x) => x + 1 }

Upvotes: 1

gpampara
gpampara

Reputation: 12049

They are failing because the types are wrong and the compiler correctly states it as such.

The map case fails because map expects a function A => B. In your code, A => B is really Int => Int which will not work because calling map on your list means that A is actually Option[Int].

Furthermore, flatMap expects a function of the form A => F[B]. So you will get your answer if you did res0 flatMap { o => o map { a => a + 1 } }. This basically is the expansion of:

for {
  element <- res0 // o above
  value <- element // a above
} yield value + 1

Upvotes: 1

Malte Schwerhoff
Malte Schwerhoff

Reputation: 12852

You try to invokee.+(1) on every element e in a List[Option[Int]], but + is not a function declared by Option[_]. String concatenation, however, would be possible (I assume there is an implicit from Any to String), but only if the second argument were a string as well (not sure why the implicit whose existence I assumed isn't considered here).

You can overcome this problem by doing working with a default value as suggested by @korefn, or "hide" the differentiation between Some(x) and None in another invocation of map, namely by

map(_.map(_ + 1))

Upvotes: 1

Daniel C. Sobral
Daniel C. Sobral

Reputation: 297195

The first two things you tried failed because you are trying to add an Option to an Int, and that's not possible.

The weird error message happens because Scala assumes, since Option doesn't have a + method, that you are trying String concatenation, but you'd have to either add an Option to a String, or a String to an Option, and you are doing neither, hence the error message.

In the last case, you are not trying to add anything, you are simply returning Option as is, hence no error message.

Upvotes: 6

sepp2k
sepp2k

Reputation: 370162

Both the function given to flatMap as well as the function given to map take a value of the list's element type - in this case Option[Int]. But your function _ + 1 expects an Int, not an Option[Int], so you can't use it as the argument to either map or flatMap in this case. Further the function given to flatMap should return an iterable¹, but your function would return a number.

This will do what you want: res0 flatMap (_ map (_ + 1)). Here the function given to flatMap takes an Option[Int] and returns an Option[Int] by calling map on the option. flatMap then takes the options returned by the function and concatenates them.

¹ Technically a GenTraversableOnce.

Upvotes: 1

Jean-Philippe Pellet
Jean-Philippe Pellet

Reputation: 59994

To increment all values that are not None, you need to map also each Option element of the list, like so:

scala> res0.map(_.map(_ + 1))
res1: List[Option[Int]] = List(Some(2), Some(3), None, Some(4))

If you want to filter out the Nones, you would indeed use a flatMap:

scala> res0.flatMap(_.map(_ + 1))
res2: List[Int] = List(2, 3, 4)

Upvotes: 1

Related Questions