Skip to content

Rewrote higher-order-functions #699

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Dec 12, 2017
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 93 additions & 12 deletions _tour/higher-order-functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,32 +13,113 @@ previous-page: mixin-class-composition
redirect_from: "/tutorials/tour/higher-order-functions.html"
---

Scala allows the definition of higher-order functions. These are functions that _take other functions as parameters_, or whose _result is a function_. Here is a function `apply` which takes another function `f` and a value `v` and applies function `f` to `v`:

Higher order functions take other functions as parameters or return a function as
a result. This is possible because functions are first-class values in Scala.
One of the most common examples is the higher-order
function `map` which is available for collections in Scala.
```tut
def apply(f: Int => String, v: Int) = f(v)
val salaries = Seq(20000, 70000, 40000)
val doubleSalary = (x: Int) => x * 2
val newSalaries = salaries.map(doubleSalary) // List(40000, 140000, 80000)
```
`doubleSalary` is a function which takes a single Int, `x`, and returns `x * 2`. In general, the tuple on the left of the arrow `=>` is a parameter list and the value of the expression on the right is what gets returned. One line 3, the function `doubleSalary` gets applied to each element in the
list of salaries.

_Note: methods are automatically coerced to functions if the context requires this._
To shrink the code, we could make the function anonymous and pass it directly as
an argument to map:
```
val salaries = Seq(20000, 70000, 40000)
val newSalaries = salaries.map(x => x * 2) // List(40000, 140000, 80000)
```
Notice how `x` is not declared as an Int in the above example. That's because the
compiler can infer the type based on the type of function map expects. An even more idiomatic way to write the same piece of code would be

Here is another example:

```tut
class Decorator(left: String, right: String) {
def layout[A](x: A) = left + x.toString() + right
val salaries = Seq(20000, 70000, 40000)
val newSalaries = salaries.map(_ * 2)
```
Since the Scala compiler already knows the type of the parameters (a single Int),
you just need to provide the right side of the function. The only
caveat is that you need to use `_` in place of a parameter name (it was `x` in
the previous example).

## Coercing methods into functions
It is also possible to pass methods as arguments to higher-order functions because
the Scala compiler will coerce the method into a function.
```
case class WeeklyWeatherForecast(temperatures: Seq[Double]) {

private def convertCtoF(temp: Double) = temp * 1.8 + 32

def forecastInFahrenheit: Double = temperatures.map(convertCtoF) // <-- passing the method convertCtoF
}
```
Here the method `convertCtoF` is passed to forecastInFahrenheit This is possible because the compiler coerces `convertCtoF` to the function `x => convertCtoF(x)` (note: `x` will
be a generated name which is guaranteed to be unique within its scope).

## Functions that accept functions
One reason to use higher-order functions is to reduce redundant code. Let's say you wanted functions that could raise someone's salaries by various factors. Without creating a higher-order function,
it might look something like this:

```tut
object SalaryRaiser {

def smallPromotion(salaries: List[Double]): List[Double] =
salaries.map(salary => salary * 1.1)

def greatPromotion(salaries: List[Double]): List[Double] =
salaries.map(salary => salary * math.log(salary))

object FunTest extends App {
def apply(f: Int => String, v: Int) = f(v)
val decorator = new Decorator("[", "]")
println(apply(decorator.layout, 7))
def hugePromotion(salaries: List[Double]): List[Double] =
salaries.map(salary => salary * salary)
}
```

Execution yields the output:

Notice how each of the three methods vary only by the multiplication factor. To simplify,
you can extract the repeated code into a higher-order function like so:

```tut
object SalaryRaiser {

private def promotion(salaries: List[Double], promotionFunction: Double => Double): List[Double] =
salaries.map(promotionFunction)

def smallPromotion(salaries: List[Double]): List[Double] =
promotion(salaries, salary => salary * 1.1)

def bigPromotion(salaries: List[Double]): List[Double] =
promotion(salaries, salary => salary * math.log(salary))

def hugePromotion(salaries: List[Double]): List[Double] =
promotion(salaries, salary => salary * salary)
}
```
[7]

The new function, `promotion`, takes the salaries plus a function of type `Double => Double`
(i.e. a function that takes a Double and returns a Double) and returns the product.

## Functions that return functions

There are certain cases where you want to generate a function. Here's an example
of a method that returns a function.

```tut
def urlBuilder(ssl: Boolean, domainName: String): (String, String) => String = {
val schema = if (ssl) "https://" else "http://"
(endpoint: String, query: String) => s"$schema$domainName/$endpoint?$query"
}

val domainName = "www.example.com"
def getURL = urlBuilder(ssl=true, domainName)
val endpoint = "users"
val query = "id=1"
val url = getURL(endpoint, query) // "https://www.example.com/users?id=1": String
```

In this example, the method `decorator.layout` is coerced automatically to a value of type `Int => String` as required by method `apply`. Please note that method `decorator.layout` is a _polymorphic method_ (i.e. it abstracts over some of its signature types) and the Scala compiler has to instantiate its method type first appropriately.
Notice the return type of urlBuilder `(String, String) => String`. This means that
the returned anonymous function takes two Strings and returns a String. In this case,
the returned anonymous function is `(endpoint: String, query: String) => s"$schema$domainName/$endpoint?$query"`.