Skip to content

Commit df6fea3

Browse files
committed
Rewrote higher-order-functions
removed illegal characters
1 parent 0874633 commit df6fea3

File tree

1 file changed

+94
-16
lines changed

1 file changed

+94
-16
lines changed

tutorials/tour/_posts/2017-02-13-higher-order-functions.md

Lines changed: 94 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,34 +9,112 @@ categories: tour
99
num: 8
1010
next-page: nested-functions
1111
previous-page: anonymous-function-syntax
12+
assumed-knowledge: sequence-comprehensions, anonymous-function-syntax
1213
---
1314

14-
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`:
15-
15+
Higher order functions take other functions as parameters or return a function as
16+
a result. This is possible because functions are first-class values in Scala.
17+
One of the most common examples is the higher-order
18+
function `map` which is available for collections in Scala.
1619
```tut
17-
def apply(f: Int => String, v: Int) = f(v)
20+
val salaries = Seq(20000, 70000, 40000)
21+
val doubleSalary = (x: Int) => x * 2
22+
val newSalaries = salaries.map(doubleSalary) // List(40000, 140000, 80000)
1823
```
24+
`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
25+
list of salaries.
1926

20-
_Note: methods are automatically coerced to functions if the context requires this._
27+
To shrink the code, we could make the function anonymous and pass it directly as
28+
an argument to map:
29+
```
30+
val salaries = Seq(20000, 70000, 40000)
31+
val newSalaries = salaries.map(x => x * 2) // List(40000, 140000, 80000)
32+
```
33+
Notice how `x` is not declared as an Int in the above example. That's because the
34+
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
2135

22-
Here is another example:
23-
2436
```tut
25-
class Decorator(left: String, right: String) {
26-
def layout[A](x: A) = left + x.toString() + right
37+
val salaries = Seq(20000, 70000, 40000)
38+
val newSalaries = salaries.map(_ * 2)
39+
```
40+
Since the Scala compiler already knows the type of the parameters (a single Int),
41+
you just need to provide the right side of the function. The only
42+
caveat is that you need to use `_` in place of a parameter name (it was `x` in
43+
the previous example).
44+
45+
## Coercing methods into functions
46+
It is also possible to pass methods as arguments to higher-order functions because
47+
the Scala compiler will coerce the method into a function.
48+
```
49+
case class WeeklyWeatherForecast(temperatures: Seq[Double]) {
50+
51+
private def convertCtoF(temp: Double) = temp * 1.8 + 32
52+
53+
def forecastInFahrenheit: Double = temperatures.map(convertCtoF) // <-- passing the method convertCtoF
2754
}
55+
```
56+
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
57+
be a generated name which is guaranteed to be unique within its scope).
58+
59+
## Functions that accept functions
60+
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,
61+
it might look something like this:
62+
63+
```tut
64+
object SalaryRaiser {
65+
66+
def smallPromotion(salaries: List[Double]): List[Double] =
67+
salaries.map(salary => salary * 1.1)
68+
69+
def greatPromotion(salaries: List[Double]): List[Double] =
70+
salaries.map(salary => salary * math.log(salary))
2871
29-
object FunTest extends App {
30-
def apply(f: Int => String, v: Int) = f(v)
31-
val decorator = new Decorator("[", "]")
32-
println(apply(decorator.layout, 7))
72+
def hugePromotion(salaries: List[Double]): List[Double] =
73+
salaries.map(salary => salary * salary)
3374
}
3475
```
35-
36-
Execution yields the output:
3776

77+
Notice how each of the three methods vary only by the multiplication factor. To simplify,
78+
you can extract the repeated code into a higher-order function like so:
79+
80+
```tut
81+
object SalaryRaiser {
82+
83+
private def promotion(salaries: List[Double], promotionFunction: Double => Double): List[Double] =
84+
salaries.map(promotionFunction)
85+
86+
def smallPromotion(salaries: List[Double]): List[Double] =
87+
promotion(salaries, salary => salary * 1.1)
88+
89+
def bigPromotion(salaries: List[Double]): List[Double] =
90+
promotion(salaries, salary => salary * math.log(salary))
91+
92+
def hugePromotion(salaries: List[Double]): List[Double] =
93+
promotion(salaries, salary => salary * salary)
94+
}
3895
```
39-
[7]
96+
97+
The new function, `promotion`, takes the salaries plus a function of type `Double => Double`
98+
(i.e. a function that takes a Double and returns a Double) and returns the product.
99+
100+
## Functions that return functions
101+
102+
There are certain cases where you want to generate a function. Here's an example
103+
of a method that returns a function.
104+
105+
```tut
106+
def urlBuilder(ssl: Boolean, domainName: String): (String, String) => String = {
107+
val schema = if (ssl) "https://" else "http://"
108+
(endpoint: String, query: String) => s"$schema$domainName/$endpoint?$query"
109+
}
110+
111+
val domainName = "www.example.com"
112+
def getURL = urlBuilder(ssl=true, domainName)
113+
val endpoint = "users"
114+
val query = "id=1"
115+
val url = getURL(endpoint, query) // "https://www.example.com/users?id=1": String
40116
```
41117

42-
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.
118+
Notice the return type of urlBuilder `(String, String) => String`. This means that
119+
the returned anonymous function takes two Strings and returns a String. In this case,
120+
the returned anonymous function is `(endpoint: String, query: String) => s"$schema$domainName/$endpoint?$query"`.

0 commit comments

Comments
 (0)