tags:

views:

946

answers:

1

I don't understand why authors said that Code Listing 9.1 from "Programming in Scala" use closure. In chapter 9, they show how to refactor code into more less duplicated form, from this original code:

object FileMatcher {
  private def filesHere = (new java.io.File(".")).listFiles
  def filesEnding(query: String) =
    for (file <- filesHere; if file.getName.endsWith(query))
      yield file
  def filesContaining(query: String) =
    for (file <- filesHere; if file.getName.contains(query))
      yield file
  def filesRegex(query: String) =
    for (file <- filesHere; if file.getName.matches(query))
      yield file
}

To the second version:

object FileMatcher {
  private def filesHere = (new java.io.File(".")).listFiles
  def filesMatching(query: String,
    matcher: (String, String) => Boolean) = {
      for (file <- filesHere; if matcher(file.getName, query))
        yield file
    }    
  def filesEnding(query: String) =
    filesMatching(query, _.endsWith(_))
  def filesContaining(query: String) =
    filesMatching(query, _.contains(_))
  def filesRegex(query: String) =
    filesMatching(query, _.matches(_))
}

Which they said that there is no use of closure here. Now I understand until this point. However they introduced the use of closure to refactor even some more, shown in Listing 9.1:

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

Now they said that query is a free variable but I don't really understand why they said so? Since ""query"" seems to be passed from top method down to string matching function explicitly.

+13  A: 

Let's look at the classic add-n closure from What is a closure.

(define (add a)
  (lambda (b)
    (+ a b)))

(define add3 (add 3))

(add3 4) returns 7

In the above lambda expression, a is the free variable, which is defined in the Wikipedia link to be:

a variable referred to in a function that is not a local variable or an argument of that function. An upvalue is a free variable that has been bound (closed over) with a closure.

Coming back to

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

The implicit function x => x.endsWith(query) is the first-class function, which is assigned to first-class value matcher, and _.endsWith() is closed over query, similar to the way 3 closes up a in (add 3). (add3 4) equivalent is done by matcher(file.getName).

Edit: Tricky part is the feature in Scala called placeholder syntax anonymous functions. By using _ in place of the sender or the parameter, Scala automatically creates an anonymous function, which we can consider it as lambda expression.

For example,

_ + 1              creates       x => x + 1
_ * _              creates       (x1, x2) => x1 * x2
_.endsWith(query)  creates       x => x.endsWith(query)

Within the function x => x.endsWith(query), query meets the two requirements of being a free variable:

  1. query is not a local variable defined within the function (there is no local variable).
  2. query is not an argument of the function (the only argument is x).
eed3si9n
Am I understand correctly that because "matcher" method capture variable "query" therefore it use closure.
Ekkmanz
Yes, in this code "def filesEnding(query: String) = filesMatching(_.endsWith(query))" there's a lambda "_.endsWith(query)" which when desugared a bit looks like "{x => x.endsWith(query)}". In schemey notation would look like "(lambda (x) (endwith x query))". As you can see, in the lambda "query" is a free variable. It's not bound as an argument nor with a let in the lambda, so when the closure is formed query is captured from the containing environment, e.g. invocations of methods like "filesEnding".
James Iry