2

I thought the dot between a collection and the map function was optional, but it seems like there is more to the story than that.

This works in both formats:

scala> List(1,2,3) map (_*2)
res6: List[Int] = List(2, 4, 6)

scala> List(1,2,3).map (_*2)
res7: List[Int] = List(2, 4, 6)

This fails:

scala> articleFiles map (file => (file \\ "contentitem").map( a => (a \ "@id").text) ).flatten
<console>:17: error: missing parameter type
       articleFiles map (file => (file \\ "contentitem").map( a => (a \ "@id").text) ).flatten
                         ^

But this works:

scala> articleFiles.map (file => (file \\ "contentitem").map( a => (a \ "@id").text) ).flatten
res7: scala.collection.immutable.Seq[String] = List(20761, 22798, 22799, 21167, 21438, 20770, 21480, 21906, 21907, 21923, 22766, 22771, 22794, 22800, 22803, 22804, 22818, 22819, 22820, 22821, 20456, 20771, 21337, 21542, 21590, 20768, 20775, 

Note, the only difference between the last 2 examples is the dot in articleFiles.map

doub1ejack
  • 10,627
  • 20
  • 66
  • 125
  • 1
    It is caused by [semicolon inference](http://stackoverflow.com/a/10313469/4496364), but you could use space instead of dot with `flatten` and it should work. This syntax is called suffix notation and it is [unsafe to use](http://docs.scala-lang.org/style/method-invocation.html). I prefer using dot notation because it is easier to read the flow and use methods such as `flatten` or `toList` etc.. – insan-e Jul 28 '16 at 17:43
  • The problem is with `.flatten` call, not with `.map`.. – insan-e Jul 28 '16 at 17:56
  • Semicolon inference is not the problem here: in both cases you get the semicolon at the end of the line, as desired. – Alexey Romanov Jul 28 '16 at 18:28
  • 1
    Try removing the flatten in the end, now try the flatten again but instead of a dot use a space. You will see that both works. – pedrofurla Jul 28 '16 at 18:58

3 Answers3

3

You can't mix calls with and without . in a single chain, so

articleFiles map (file => (file \\ "contentitem").map( a => (a \ "@id").text) ).flatten

means

articleFiles.map((file => (file \\ "contentitem").map( a => (a \ "@id").text) ).flatten)

which obviously won't work: functions don't have a flatten method. This specific error happens because the argument type in an anonymous function can only be left out when it's used in a context where a function type is expected, and this isn't such a context.

Alexey Romanov
  • 167,066
  • 35
  • 309
  • 487
  • I'd also add working example: `vs map(Option.apply) flatten` (this will show warning about advanced language feature though) – dk14 Jul 28 '16 at 23:02
  • the only potential problem with suffix notation is semicolon inference – dk14 Jul 28 '16 at 23:07
3

Just to add that there are a couple of tools for showing what happened to your code, in particular, what parens and types are inferred.

scala> val vs = (1 to 10).toList
vs: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

scala> vs.map(Option.apply)
res0: List[Option[Int]] = List(Some(1), Some(2), Some(3), Some(4), Some(5), Some(6), Some(7), Some(8), Some(9), Some(10))

scala> vs.map(Option.apply).flatten
res1: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

scala> vs map(Option.apply).flatten
<console>:13: error: missing argument list for method apply in object Option
Unapplied methods are only converted to functions when a function type is expected.
You can make this conversion explicit by writing `apply _` or `apply(_)` instead of `apply`.
       vs map(Option.apply).flatten
                     ^

scala> vs map(Option.apply(_)).flatten
<console>:13: error: missing parameter type for expanded function ((x$1: <error>) => Option.apply(x$1))
       vs map(Option.apply(_)).flatten
                           ^

scala> vs map(Option.apply(_)).flatten // show
[snip]
      val res4 = vs.map(((x$1) => Option apply x$1).flatten)

<console>:13: error: missing parameter type for expanded function ((x$1: <error>) => Option.apply(x$1))
       vs map(Option.apply(_)).flatten // show
                           ^
som-snytt
  • 39,429
  • 2
  • 47
  • 129
  • The compiler should say that there is an enclosing context with expected type a function. Similar to http://stackoverflow.com/q/38611092/1296806. – som-snytt Jul 28 '16 at 22:38
  • 1
    that `// show` comment-directive is very cool, but it looks like a magic - so I'd explicitly describe what it does in the answer as one might think that this is just a comment (especially if you forget to put a space between `//` and `show`) – dk14 Jul 28 '16 at 23:12
  • @dk14 I fixed the annoying space thing recently. I try to spread the tips, but, too lazy for complete answers to SO's high standards. SO needs an AI to merge duplicates and consolidate info. Maybe I'll add something http://docs.scala-lang.org/overviews/repl/overview.html – som-snytt Jul 29 '16 at 00:35
1

In Scala, operators are methods and can be treated as such. We can either use the period to invoke map as a method, or simply use it as a an operator. This is a good explanation.
As far as why the first fails and the second does not, the second example is much less ambiguous than the first. The compiler knows what variable type to expect with the method invocation, but with the operator invocation it is more ambiguous (could be AnyVal) and thus it throws missing parameter type. Simply put, when stacking the maps and using different invocations the compiler cannot decide what variable type it should be using.

nldoty
  • 471
  • 3
  • 8