© Toby Weston 2018

Toby Weston, Scala for Java Developers, https://doi.org/10.1007/978-1-4842-3108-1_15

15. Faking Language Constructs

Toby Weston

(1)London, UK

Scala allows you to write your code in such a way as to give the impression that you’re working with native language constructs , when really you’re just working with regular methods.

This chapter will cover the following:

  • How Scala allows you to use curly braces instead of regular parentheses when calling methods.

  • How Scala supports higher-order functions: functions that take functions as arguments and return functions as results.

  • How Scala supports currying out of the box.

These things don’t sound that impressive, but combined they allow for a surprising amount of flexibility. We’ll see how these techniques can help you write more flexible and readable code.

All the code samples in this chapter are in Scala.

Curly Braces (and Function Literals)

There’s a simple rule in Scala .

  • Any method call that accepts exactly one argument can use curly braces to surround the argument instead of parentheses.

So, instead of this:

  numerals.foreach(println(_))

…you can write this:

  numerals.foreach{println(_)}

All we’ve done is swap the brackets for curly braces. Not very impressive, but things start to look a bit more interesting when we introduce some new lines.

  numerals.foreach {
    println(_)
  }

Now it begins to look like a built-in control structure. Developers are used to interpreting curly braces as demarcation of language syntax. So, this looks more like the built-in for loop, even though it’s just a method call.

The main reason for doing this is to allow clients to pass in functions as arguments in a natural and concise way. When you write functions that can take functions as arguments, you’re creating higher-order functions. These allow for greater flexibility and re-use.

For example, let’s say we want to do some work and update a UI element, like a progress bar or a customer basket. The best way to do this is in a new thread so that we don’t slow down the main UI thread and cause pauses for the user.

Higher-Order Functions

If every call to update a UI element must be done on its own thread, we might end up with a naive implementation like this:

  object Ui {
    def updateUiElements() {
      new Thread() {
        override def run(): Unit = updateCustomerBasket(basket)
      }.start()


      new Thread() {
        override def run(): Unit = updateOffersFor(customer)
      }.start()
    }
  }

The Ui object executes the sequence of updates one after another, each on a new thread. The Ui object is managing the threading policy and the update behavior. It would be better if something else was responsible for coordinating threading and the Ui object was left to the update behavior. That way, we could avoid duplication and if the threading policy changes, we wouldn’t have to find all the usages scattered about the place.

The solution is to define a function that can run some other function on a thread. We could create a function called runInThread with the boilerplate threading code.

  def runInThread() {
    new Thread() {
      override def run(): Unit = ???
    }.start()
  }

It will create and start a new thread but it doesn’t do anything interesting. How do we pass in a function? In Java, you’d probably pass in an anonymous instance of a Runnable or Callable or a lambda.

You do the same in Scala but rather than pass in a functional interface as the argument, you pass in a shorthand signature denoting a function argument. You define a variable as usual (function in the following example) but the type that follows the colon represents a function. Our example has no arguments and returns a value of Unit. It’s equivalent to Java’s signature for a lambda: () -> Void.

  def runInThread(function: () => Unit) {
    new Thread() {
      override def run(): Unit = ???
    }.start()
  }

Then we just execute the function in the body of the thread. Remember the brackets denote the shorthand for executing the apply method.

  def runInThread(function: () => Unit) {
    new Thread() {
      override def run(): Unit = function()       // aka function.apply()
    }.start()
  }

Given the new runInThread method, we can rewrite the UI code like this:

  def updateUiElements() {
    runInThread(() => updateCustomerBasket(basket))
    runInThread(() => updateOffersFor(customer))
  }

We’ve eliminated the duplication by passing in functions to runInThread.

Higher-Order Functions with Curly Braces

This doesn’t really live up to the promise of clients being able to pass functions as arguments “in a natural and concise way”. It looks a lot like Java’s lambda syntax, but we can make it look more natural and more like language syntax if we use the curly braces.

If we just replace the parentheses with curly braces, it doesn’t really improve things.

  // yuk!
  def updateUiElements() {
    runInThread { () =>
      updateCustomerBasket(basket)
    }


    runInThread { () =>
      updateOffersFor(customer)
    }
  }

But we can employ another trick to get rid of the empty parentheses and arrows. We can use what’s called a call-by-name parameter.

Call-by-Name

In Java, you can’t do anything about an empty lambda argument list (e.g., () -> Void) but in Scala, you can drop the brackets from a function signature to indicate that the argument is call-by-name . To invoke it, you no longer need to call the apply method. Instead, you simply reference it.

  def runInThread(function: => Unit) {         // call-by-name
    new Thread() {
      override def run(): Unit = function      // not function()
    }.start()
  }

The by-name parameter expression isn’t evaluated until it’s actually used; not when it’s defined. It behaves just like the longhand function did even though it looks like we’re calling the function at the point where we pass it into our runInThread method.

  def updateUiElements() {
    runInThread {
      updateCustomerBasket(basket)
    }


    runInThread {
      updateOffersFor(customer)
    }
  }

This starts to make things look a lot more natural, especially if we want to do more within a running thread. For example, let’s say we want to apply a discount before updating a customer’s basket. The braces and indents make it very clear that this happens in the same thread as the update.

  def updateUiElements() {
    runInThread {
      applyDiscountToBasket(basket)
      updateCustomerBasket(basket)
    }
    runInThread {
      updateOffersFor(customer)
    }
  }

You can think of it as shorthand for creating a parameter-less lambda.

Currying

Using the apply method and curly braces allows us to create APIs that are expressive and natural to use. It allows us to create control abstractions that conform to what we already expect from the language in terms of syntax.

But remember what we said earlier about the curly braces rule.

  • Any method call that accepts exactly one argument can use curly braces to surround the argument instead of parentheses.

We can only use curly braces with single-argument methods. What if we want to add an argument to our runInThread method and still use the elegant syntax? The good news is that it’s entirely possible; we employ a technique called currying.

Let’s extend our runInThread method to add a new argument to assign a thread group.

  def runInThread(group: String, function: => Unit) {
    new Thread(new ThreadGroup(group), new Runnable() {
      def run(): Unit = function
    }).start()
  }

As only single-argument lists can use braces, we have to regress the Ui object back to using parentheses.

  // yuk!
  def updateUiElements() {
    runInThread("basket", {
      applyDiscountToBasket(basket)
      updateCustomerBasket(basket)
    })
    runInThread("customer",
      updateOffersFor(customer)
    )
  }

If we could convert our function with two arguments into a function that takes one argument we’d be able to use the curly braces again. Fortunately for us, that’s exactly what currying is about. Currying is the process of turning a function of two or more arguments into a series of functions, each taking a single argument.

For a function of two arguments, currying would produce a function that takes one argument and returns another function. This returned function would also have a single argument (for what would have been the second argument of the original function). Confused? Let’s work through an example.

Let’s say we have a function f that takes two arguments, a and b, and returns a + b.

f (a, b) = a + b

To convert this into two functions, each with a single argument, first we create a function to take a and give back a new function (f′).

f (a) → f  ′

This new function should itself take a single argument, b.

f (a) → f  ′ (b)

That entire function should return the result, a + b.

f (a) → f  ′ (b) → a + b

We’re left with two functions (f and →f  ′), each taking a single argument.

With the pseudo-mathematical notation on the same page, it’s worth restating my original definition and comparing the original to the curried form of the function (see Figure 15-1).

  • For a function of two arguments, currying would produce a function that takes one argument and returns another function. This returned function would also have a single argument (for what would have been the second argument of the original function).

A456960_1_En_15_Fig1_HTML.jpg
Figure 15-1 Original function and steps to arrive at its curried form

To evaluate the functions of the curried form, we’d evaluate the first function (for example, passing in a value 1).

f (1)

This would return a function that captures the value, and, because what’s returned is a function, we can just evaluate it, providing a value for the last argument (2).

f (1)(2)

At this point, both values are in scope and any computation can be applied giving the final result.

I’ve been using a bit of a gorilla notation1 to get my point across here. Using a more mathematically correct notation, we could show the function as being curried by creating a new function taking a and mapping b to a + b.

f (a) = (b → a + b)

If you’re familiar with the lambda calculus,2 you’ll already know that λab.a + b is shorthand for its curried form λa. (λb.(a + b)).

Scala Support for Curried Functions

A regular uncurried function to add two numbers might look like this:

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

Scala supports curried functions out of the box, so we don’t need to do any manual conversion; all we do to turn this into its curried version is to separate out the arguments using parentheses.

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

Scala has created two single-argument parameter lists for us. To evaluate the function, we’d do the following:

  scala> add(1)(2)
  res1: Int = 3

To see it in stages, we could just evaluate the first half like this:

  scala> val f = add(1) _
  f: Int => Int = <function1>

The underscore gives the REPL a hint about what we’re trying to do. The result f is a function from Int to Int. The value 1 has been captured and is available to that function. So, we can now just execute the returned function supplying our second value.

  scala> f(2)
  res2: Int = 3

So, what does this mean for our runInThread method? Well, if we create a curried version of the function, we can get back to using our lovely curly braces.

We start by splitting the argument into two to create the curried form of the original.

  def runInThread(group: String)(function: => Unit) {
    new Thread(new ThreadGroup(group), new Runnable() {
      def run(): Unit = function
    }).start()
  }

Notice there are no other changes to make to the function. Inside runInThread everything is just as it was. However, we can now change the Ui object back to using curly braces for the second argument.

  def updateUiElements() {
    runInThread("basket") {
      applyDiscountToBasket(basket)
      updateCustomerBasket(basket)
    }
    runInThread("customer",
      updateOffersFor(customer)
    )
  }

Summary

With a few built-in features, Scala allows us to write methods that look like language constructs. We can use higher-order functions to create control abstractions: functions that abstract over complex behavior and reduce duplication yet still offer flexibility to the code that calls them.

We can use curly braces anywhere a single-argument method is used. We can use this to provide visual cues and patterns that are immediately recognizable. Using built-in currying support, we’re not limited to using this only for single-argument functions; we can create even richer APIs by converting multiple-argument functions into multiple single-argument functions.

Footnotes

1 A discussion of the notation used can be found at http://bit.ly/1Q2bU6s

2 Some notes on the Lambda Calculus can be found at http://bit.ly/1G4OdVo

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.119.162.49