3

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 Answers 2

3

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:

Sign up to request clarification or add additional context in comments.

4 Comments

The thing that's mysterious (at least to me) is why does val number = (StdIn.readLine().split(" ").map(_.toInt))(1) not work? The search for the implicit parameter extends outside the parentheses as if they are not there. I can't find an explanation in the Scala spec.
@JoePallas updated my answer, hopefully that clears up the confusion.
the updated answer opens a can of worms because I'm not sure we can talk about the type of add(2) without doing the explicit eta-expansion add(2) _. And it looks like that's where part of the confusion comes from: implicit resolution occurs before eta-expansion (stackoverflow.com/a/18635935/3851755). Interestingly, that leads to an alternative solution: (StdIn.readLine().split(" ").map(_.toInt) _)(1)
@JoePallas sure, on its own add(2) is not a valid expression, e.g. val x = add(2) will not compile. however, i'm referring to add(2) as a smaller part of bigger expression. the point i'm trying to make is add(2)(3) is the same as (add(2))(3) is the same as ((add(2)))(3) and so on... the second arg list must be known before using shorthand for apply since the invocation of apply is done on the result of the expression. i.e. invoking apply using shorthand notation clashes with explicit specification of the second arg list...
1

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()()

3 Comments

So how to invoke makeFooImplicit?
@BartłomiejSzałach both makeFooImplicit() and makeFooImplicit()(1) will return an instance of Foo. the former works because implicit val x = 5 is defined which effectively means makeFooImplicit()(5).
You could say val f = makeFooImplicit(); f() and get the printout.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.