2

In the below two scenarios, I have flatMap function invoked on a list. In both the cases, the map portion of the flatMap function returns an array which has a iterator. In the first case, the code errors out where in the second case, it produces the expected result.

Scenario-1

val x = List("abc","cde")
x flatMap ( e => e.toArray)
<console>:13: error: polymorphic expression cannot be instantiated to expected type;
 found   : [B >: Char]Array[B]
 required: scala.collection.GenTraversableOnce[?]
       x flatMap ( e => e.toArray)

Scenario-2

val x = List("abc,def")
x flatMap ( e => e.split(",") )
res1: List[String] = List(abc, def) //Result

Can you please help why in the first case, it is not behaving as expected ?

user3103957
  • 636
  • 4
  • 16
  • 3
    Interesting. `flatMap()` won't flatten a `List[Array[Char]]` (scenario-1) but it _will_ flatten a `List[Array[String]]` (scenario-2). In both cases if you change `flatMap()` to `map()` and then add `.flatten` at the end, they both work. – jwvh May 03 '20 at 09:09
  • @jwvh yes, that works correctly. I am bit puzzled in the above context. – user3103957 May 03 '20 at 09:17
  • 2
    I think the difference is that in scenario 1 you actually have a `Array[B]` where `B` is some yet-to-be-decided supertype of `Char` which in combination with the search for an implicit conversion from `Array` to `GenTraversableOnce` causes a type inference issue. By the way you can just call `List("abc", "cde").flatten`. – Jasper-M May 03 '20 at 09:24

4 Answers4

3

I think the difference is that in scenario 1 you actually have a Array[B] where B is some yet-to-be-decided supertype of Char. Normally the compiler would look for an implicit conversion to GenTraversableOnce but because B is not yet known you run into a type inference issue / limitation.

You can help type inference by filling in B.

List("abc", "cde").flatMap(_.toArray[Char])

Or even better, you don't need flatMap in this case. Just call flatten.

List("abc", "cde").flatten
Jasper-M
  • 14,966
  • 2
  • 26
  • 37
3

It is worth keeping in mind that Array is not a proper Scala collection so compiler has to work harder to first convert it to something that fits with the rest of Scala collections

implicitly[Array[Char] <:< GenTraversableOnce[Char]] // error (Scala 2.12)
implicitly[Array[Char] <:< IterableOnce[Char]]       // error (Scala 2.13)

Hence because flatMap takes a function

String => GenTraversableOnce[Char]

but we are passing in

String => Array[Char]

the compiler first has to find an appropriate implicit conversion of Array[Char] to GenTraversableOnce[Char]. Namely this should be wrapCharArray

scala.collection.immutable.List.apply[String]("abc", "cde").flatMap[Char](((e: String) => scala.Predef.wrapCharArray(scala.Predef.augmentString(e).toArray[Char]((ClassTag.Char: scala.reflect.ClassTag[Char])))))

or shorter

List("abc", "cde").flatMap(e => wrapCharArray(augmentString(e).toArray))

however due to type inference reasons explained by Jasper-M, the compiler is unable to select wrapCharArray conversion. As Daniel puts it

Array tries to be a Java Array and a Scala Collection at the same time. It mostly succeeds, but fail at both for some corner cases.

Perhaps this is one of those corner cases. For these reasons it is best to avoid Array unless performance or compatibility reasons dictate it. Nevertheless another approach that seems to work in Scala 2.13 is to use to(Collection) method

List("abc","cde").flatMap(_.to(Array))
Mario Galic
  • 47,285
  • 6
  • 56
  • 98
0

As the error mentions, your type is wrong.

In the first case, if you appy map not flatmap, you obtain List[Array[Char]]. If you apply flatten to that, you get a List[Char]

In the second case, if you appy map not flatmap, you obtain List[Array[String]]. If you apply flatten to that, you get a List[String]

I suppose you need to transform String to Char in your Array, in order to make it work.

I use Scala 2.13 and still have the same error.

Catalina Chircu
  • 1,506
  • 2
  • 8
  • 19
  • Thanks for looking into this. But even in my second case, the functional literal I supply to flatMap, produces an Array. – user3103957 May 03 '20 at 09:09
0
scala> val x = List("abc","cde")
x: List[String] = List(abc, cde)

scala> x.flatMap[Char](_.toArray)
res0: List[Char] = List(a, b, c, c, d, e)
Andriy Plokhotnyuk
  • 7,883
  • 2
  • 44
  • 68
  • thanks for your response. I just copy & paste your snippet in my Scala console. It gives error ( wrong number of type parameters for method flatMap ...) . I have Scala 2.12.8. Is this fixed in higher version of Scala ? – user3103957 May 03 '20 at 09:15
  • 1
    I run your code on my computer and I have 2.13. I updated my answer with what I get. – Catalina Chircu May 03 '20 at 09:26