2
votes

Accessing collection elements in Scala is done by apply method. Having said that I tried to read numbers from standard input and get the second one as int in just one line.

  def main(args: Array[String]): Unit = {
    val number = StdIn.readLine().split(" ").map(_.toInt)(1)
  }

IntelliJ marks this 1 and show the error (the same is shown after the compilation attempt):

Error:(11, 60) type mismatch; found : Int(1) required: scala.collection.generic.CanBuildFrom[Array[String],Int,?] val number = StdIn.readLine().split(" ").map(_.toInt)(1)

Folding the expression in the parentheses dosn't help as well. However, when I split mapping input to int array and getting the element to other lines, everything works fine.

  def main(args: Array[String]): Unit = {
    val numbers = StdIn.readLine().split(" ").map(_.toInt)
    numbers(1)
  }

The explicit invocation of apply also does the job:

val number = StdIn.readLine().split(" ").map(_.toInt).apply(1)

Why does this weird behaviour happens? Obtaining element via array(5) is just a shortcut for array.apply(5), isn't it?

2

2 Answers

3
votes

As compiler already pointed out, map is defined with two arg lists. second one containing a single implicit param: implicit bf: CanBuildFrom[Repr, B, That]

For a vast majority of scenarios, the users don't worry about specifying the second arg. Specifically, in your case this is what the compiler "figures out":

StdIn.readLine().split(" ").map(_.toInt)(Array.canBuildFrom[Int])

So, if writing out apply is not desired then the following is the only choice:

StdIn.readLine().split(" ").map(_.toInt)(Array.canBuildFrom[Int])(1)

It's a bit counter-intuitive that adding a set of parentheses still doesn't quite do the trick:

( StdIn.readLine().split(" ").map(_.toInt) )(1)  //does not compile, same error

Perhaps it's best to demonstrate what's going with a simpler example. Consider the following function:

def add(x:Int)(y:Int) = x + y

The type of add(2) is Int => Int (since we haven't specified y). Note that adding a set of parentheses around it doesn't change the return type, i.e. (add(2)) still has a type Int => Int. Similarly, (Array("1").map(_.toInt))(1) still requires an instance of CanBuildFrom before using shorthand for apply.


Suggested reading about Scala collections:

and implicit scope:

1
votes

You're getting a collision with an implicit argument to map. This is one of the cases apply needs to be invoked explicitly.

Here's some REPL that replicates the error:

scala> class Foo {
  def apply() { println("bar") }
}

scala> def makeFoo(): Foo = new Foo

scala> makeFoo()()
bar // got the println from the apply here

scala> def makeFooImplicit()(implicit x: Int): Foo = new Foo

scala> implicit val x = 5

scala> makeFooImplicit()()
  <console>:11: error: not enough arguments for method makeFooImplicit:(implicit x: Int)Foo.
  Unspecified value parameter x.
          makeFooImplicit()()