Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Question on Scala Closure (From "Programming in Scala")

Tags:

closures

scala

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.

like image 301
Ekkmanz Avatar asked Jul 05 '09 04:07

Ekkmanz


1 Answers

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).
like image 154
Eugene Yokota Avatar answered Sep 19 '22 07:09

Eugene Yokota