Skip to content

Commit 8e87cbb

Browse files
authored
Add code tabs for _tour/higher-order-functions
1 parent b4b509f commit 8e87cbb

File tree

1 file changed

+108
-3
lines changed

1 file changed

+108
-3
lines changed

Diff for: _tour/higher-order-functions.md

+108-3
Original file line numberDiff line numberDiff line change
@@ -20,27 +20,49 @@ In a pure Object Oriented world a good practice is to avoid exposing methods par
2020

2121
One of the most common examples is the higher-order
2222
function `map` which is available for collections in Scala.
23+
24+
{% tabs map_example_1 %}
25+
26+
{% tab 'Scala 2 and 3' for=map_example_1 %}
2327
```scala mdoc
24-
val salaries = Seq(20000, 70000, 40000)
28+
val salaries = Seq(20_000, 70_000, 40_000)
2529
val doubleSalary = (x: Int) => x * 2
2630
val newSalaries = salaries.map(doubleSalary) // List(40000, 140000, 80000)
2731
```
32+
{% end tab %}
33+
34+
{% end tabs %}
35+
2836
`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. On line 3, the function `doubleSalary` gets applied to each element in the
2937
list of salaries.
3038

3139
To shrink the code, we could make the function anonymous and pass it directly as
3240
an argument to map:
41+
42+
{% tabs map_example_2 %}
43+
44+
{% tab 'Scala 2 and 3' for=map_example_2 %}
3345
```scala:nest
34-
val salaries = Seq(20000, 70000, 40000)
46+
val salaries = Seq(20_000, 70_000, 40_000)
3547
val newSalaries = salaries.map(x => x * 2) // List(40000, 140000, 80000)
3648
```
49+
{% end tab %}
50+
51+
{% end tabs %}
3752
Notice how `x` is not declared as an Int in the above example. That's because the
3853
compiler can infer the type based on the type of function map expects (see [Currying](/tour/multiple-parameter-lists.html)). An even more idiomatic way to write the same piece of code would be:
3954

55+
{% tabs map_example_3 %}
56+
57+
{% tab 'Scala 2 and 3' for=map_example_3 %}
4058
```scala mdoc:nest
41-
val salaries = Seq(20000, 70000, 40000)
59+
val salaries = Seq(20_000, 70_000, 40_000)
4260
val newSalaries = salaries.map(_ * 2)
4361
```
62+
{% end tab %}
63+
64+
{% end tabs %}
65+
4466
Since the Scala compiler already knows the type of the parameters (a single Int),
4567
you just need to provide the right side of the function. The only
4668
caveat is that you need to use `_` in place of a parameter name (it was `x` in
@@ -49,6 +71,10 @@ the previous example).
4971
## Coercing methods into functions
5072
It is also possible to pass methods as arguments to higher-order functions because
5173
the Scala compiler will coerce the method into a function.
74+
75+
{% tabs Coercing_methods_into_functions %}
76+
77+
{% tab 'Scala 2' for=Coercing_methods_into_functions %}
5278
```scala mdoc
5379
case class WeeklyWeatherForecast(temperatures: Seq[Double]) {
5480

@@ -57,13 +83,30 @@ case class WeeklyWeatherForecast(temperatures: Seq[Double]) {
5783
def forecastInFahrenheit: Seq[Double] = temperatures.map(convertCtoF) // <-- passing the method convertCtoF
5884
}
5985
```
86+
{% end tab %}
87+
88+
{% tab 'Scala 3' for=Coercing_methods_into_functions %}
89+
```scala
90+
case class WeeklyWeatherForecast(temperatures: Seq[Double]):
91+
92+
private def convertCtoF(temp: Double) = temp * 1.8 + 32
93+
94+
def forecastInFahrenheit: Seq[Double] = temperatures.map(convertCtoF) // <-- passing the method convertCtoF
95+
```
96+
{% end tab %}
97+
98+
{% end tabs %}
99+
60100
Here the method `convertCtoF` is passed to the higher order function `map`. This is possible because the compiler coerces `convertCtoF` to the function `x => convertCtoF(x)` (note: `x` will
61101
be a generated name which is guaranteed to be unique within its scope).
62102

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

107+
{% tabs Functions_that_accept_functions_1 %}
108+
109+
{% tab 'Scala 2' for=Functions_that_accept_functions_1 %}
67110
```scala mdoc
68111
object SalaryRaiser {
69112

@@ -77,10 +120,31 @@ object SalaryRaiser {
77120
salaries.map(salary => salary * salary)
78121
}
79122
```
123+
{% end tab %}
124+
125+
{% tab 'Scala 3' for=Functions_that_accept_functions_1 %}
126+
```scala
127+
object SalaryRaiser:
128+
129+
def smallPromotion(salaries: List[Double]): List[Double] =
130+
salaries.map(salary => salary * 1.1)
131+
132+
def greatPromotion(salaries: List[Double]): List[Double] =
133+
salaries.map(salary => salary * math.log(salary))
134+
135+
def hugePromotion(salaries: List[Double]): List[Double] =
136+
salaries.map(salary => salary * salary)
137+
```
138+
{% end tab %}
139+
140+
{% end tabs %}
80141

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

145+
{% tabs Functions_that_accept_functions_2 %}
146+
147+
{% tab 'Scala 2' for=Functions_that_accept_functions_2 %}
84148
```scala mdoc:nest
85149
object SalaryRaiser {
86150

@@ -97,6 +161,27 @@ object SalaryRaiser {
97161
promotion(salaries, salary => salary * salary)
98162
}
99163
```
164+
{% end tab %}
165+
166+
{% tab 'Scala 3' for=Functions_that_accept_functions_2 %}
167+
```scala
168+
object SalaryRaiser:
169+
170+
private def promotion(salaries: List[Double], promotionFunction: Double => Double): List[Double] =
171+
salaries.map(promotionFunction)
172+
173+
def smallPromotion(salaries: List[Double]): List[Double] =
174+
promotion(salaries, salary => salary * 1.1)
175+
176+
def greatPromotion(salaries: List[Double]): List[Double] =
177+
promotion(salaries, salary => salary * math.log(salary))
178+
179+
def hugePromotion(salaries: List[Double]): List[Double] =
180+
promotion(salaries, salary => salary * salary)
181+
```
182+
{% end tab %}
183+
184+
{% end tabs %}
100185

101186
The new method, `promotion`, takes the salaries plus a function of type `Double => Double`
102187
(i.e. a function that takes a Double and returns a Double) and returns the product.
@@ -108,6 +193,9 @@ Methods and functions usually express behaviours or data transformations, theref
108193
There are certain cases where you want to generate a function. Here's an example
109194
of a method that returns a function.
110195

196+
{% tabs Functions_that_return_functions %}
197+
198+
{% tab 'Scala 2' for=Functions_that_return_functions %}
111199
```scala mdoc
112200
def urlBuilder(ssl: Boolean, domainName: String): (String, String) => String = {
113201
val schema = if (ssl) "https://" else "http://"
@@ -120,6 +208,23 @@ val endpoint = "users"
120208
val query = "id=1"
121209
val url = getURL(endpoint, query) // "https://www.example.com/users?id=1": String
122210
```
211+
{% end tab %}
212+
213+
{% tab 'Scala 3' for=Functions_that_return_functions %}
214+
```scala
215+
def urlBuilder(ssl: Boolean, domainName: String): (String, String) => String =
216+
val schema = if ssl then "https://" else "http://"
217+
(endpoint: String, query: String) => s"$schema$domainName/$endpoint?$query"
218+
219+
val domainName = "www.example.com"
220+
def getURL = urlBuilder(ssl=true, domainName)
221+
val endpoint = "users"
222+
val query = "id=1"
223+
val url = getURL(endpoint, query) // "https://www.example.com/users?id=1": String
224+
```
225+
{% end tab %}
226+
227+
{% end tabs %}
123228

124229
Notice the return type of urlBuilder `(String, String) => String`. This means that
125230
the returned anonymous function takes two Strings and returns a String. In this case,

0 commit comments

Comments
 (0)