diplosaurus
diplosaurus

Reputation: 2588

Scala use of underscore as an object placeholder

Trying to wrap my head around the varying uses of the _. Right now I'm struggling with this example:

object Chapter9 extends App {
  FileMatcher.filesEnding(".scala").foreach(println)
}

object FileMatcher {
  private def filesHere = (new java.io.File(".")).listFiles

  private def filesMatching(matcher: String => Boolean) = {
    for (file <- filesHere; if matcher(file.getName))
    yield file
  }

  def filesEnding(query: String) =
    filesMatching(_.endsWith(query))

  def filesContaining(query: String) =
    filesMatching(_.contains(query))

  def filesRegex(query: String) =
    filesMatching(_.matches(query))
}

So clearly we want to abstract away the common work of looping/filtering/yielding for the varying types of matchers, makes sense to put it in a helper function.

I'm getting hung up on the _.endsWith part. My understanding is that this underscore (being the first and only one used in the method body) will be filled in by the first parameter, which in this case is query. I tried to test this theory by doing:

def filesEnding(query: String) = {
  println(_: String)
}

But the program doesn't print anything. So what is _ here? How does Scala know what object to to search for an endsWith method on?

It looks like from output of the program that somehow file gets filled in for this underscore but have no idea how. Maybe the underscore remains a "wildcard" until it is used inside filesMatching's body and by that point the nearest enclosing scope is the for and the first "parameterisfile`?

Upvotes: 1

Views: 553

Answers (2)

som-snytt
som-snytt

Reputation: 39577

The canonical answer is What are all the uses of an underscore in Scala?

But -Xprint:parser shows

((x$1: String) => println((x$1: String)))

which is uninteresting except for the redundantly typed expression in the body of the function.

It doesn't seem to generate any extra code. The param is already a String.

I don't think your example compiles? Or I don't know what you're asking.

Explicit types can help debug when type of an anonymous function aren't inferred as you wish.

Edit: I gave this a try:

object Chapter9 extends App {
  FileMatcher.filesEnding(".scala").foreach(println)
}

object FileMatcher {
  private def filesHere = (new java.io.File(".")).listFiles

  private def filesMatching(matcher: String => Boolean) = {
    for (file <- filesHere; if matcher(file.getName))
    yield file
  }
def filesEnding(query: String) = {
  println(_: String)
}
}

An expression with an underscore as an anonymous function needs its expected type to tell it what type the underscore is, unless explicitly annotated as you did. But that is not common usage.

Instead of (_: Int) * 2, (i: Int) => i * 2, but that's a style question.

Upvotes: 0

jwvh
jwvh

Reputation: 51271

Look at the signature for filesMatching(). Notice that it takes one argument of type String => Boolean. So its argument is a function that itself take a String argument and turns it into a Boolean.

Now remember that an anonymous function often looks something like this:

{ x => /* do something with x */ }

And in cases where x is used only once, then that can be abbreviated to a single _. So, working backwards, this

filesMatching(_.endsWith(query))

can be rewritten as this

filesMatching(x => x.endsWith(query))

So the filesMatching() code has its argument, a function that takes a string (which in the anonymous function I've called x). That function, matcher, is invoked with the string file.getName to get a Boolean. That boolean value is tested in an if clause:

if matcher(file.getName)

TL;DR: The underscore is shorthand for the file.getName string.

Upvotes: 2

Related Questions