Skip to content

Commit c7ef1d6

Browse files
committed
improve fun-eta-expanion.md wording
1 parent 005ae21 commit c7ef1d6

File tree

1 file changed

+59
-43
lines changed

1 file changed

+59
-43
lines changed

_overviews/scala3-book/fun-eta-expansion.md

+59-43
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
11
---
2-
title: Eta Expansion
2+
title: Eta-Expansion
33
type: section
4-
description: This page discusses Eta Expansion, the Scala technology that automatically and transparently converts methods into functions.
4+
description: This page discusses Eta-Expansion, the Scala technology that automatically and transparently converts methods into functions.
55
languages: [ru, zh-cn]
66
num: 31
77
previous-page: fun-function-variables
88
next-page: fun-hofs
99
---
1010

1111

12-
When you look at the Scaladoc for the `map` method on Scala collections classes, you see that it’s defined to accept a _function_:
12+
When you look at the Scaladoc for the `map` method on Scala collections classes, you see that it’s defined to accept a _function_ value:
1313

1414
{% tabs fun_1 %}
1515
{% tab 'Scala 2 and 3' for=fun_1 %}
1616

1717
```scala
18-
def map[B](f: (A) => B): List[B]
19-
-----------
18+
def map[B](f: A => B): List[B]
19+
// ^^^^^^ function type from `A` to `B`
2020
```
2121

2222
{% endtab %}
@@ -26,7 +26,7 @@ Indeed, the Scaladoc clearly states, “`f` is the _function_ to apply to each e
2626
But despite that, somehow you can pass a _method_ into `map`, and it still works:
2727

2828
{% tabs fun_2 %}
29-
{% tab 'Scala 2 and 3' for=fun_2 %}
29+
{% tab 'Scala 2 and 3' %}
3030

3131
```scala
3232
def times10(i: Int) = i * 10 // a method
@@ -36,80 +36,96 @@ List(1, 2, 3).map(times10) // List(10,20,30)
3636
{% endtab %}
3737
{% endtabs %}
3838

39-
Have you ever wondered how this works---how you can pass a _method_ into `map`, which expects a _function_?
40-
41-
The technology behind this is known as _Eta Expansion_.
39+
Why does this work? The process behind this is known as _eta-expansion_.
4240
It converts an expression of _method type_ to an equivalent expression of _function type_, and it does so seamlessly and quietly.
4341

4442
## The differences between methods and functions
4543

46-
{% comment %}
47-
NOTE: I got the following “method” definition from this page (https://dotty.epfl.ch/docs/reference/changed-features/eta-expansion-spec.html), but I’m not sure it’s 100% accurate now that methods can exist outside of classes/traits/objects.
48-
I’ve made a few changes to that description that I hope are more accurate and up to date.
49-
{% endcomment %}
50-
51-
Historically, _methods_ have been a part of the definition of a class, although in Scala 3 you can now have methods outside of classes, such as [Toplevel definitions][toplevel] and [extension methods][extension].
44+
The key difference between methods and functions is that _a function is an object_, i.e. it is an instance of a class, and in turn has its own methods (e.g. try `f.apply` on a function `f`).
5245

53-
Unlike methods, _functions_ are complete objects themselves, making them first-class entities.
46+
_Methods_ are not values that can be passed around, i.e. they can only be called via method application (e.g. `foo(arg1, arg2, ...)`). Methods can be _converted_ to a value by creating a function value that will call the method when supplied with the required arguments. This is known as eta-expansion.
5447

55-
Their syntax is also different.
56-
This example shows how to define a method and a function that perform the same task, determining if the given integer is even:
48+
More concretely: with automatic eta-expansion, the compiler automatically converts any _method reference_, without supplied arguments, to an equivalent _anonymous function_ that will call the method. For example, the reference to `times10` in the code above gets rewritten to `x => times10(x)`, as seen here:
5749

58-
{% tabs fun_3 %}
59-
{% tab 'Scala 2 and 3' for=fun_3 %}
50+
{% tabs fun_2_expanded %}
51+
{% tab 'Scala 2 and 3' %}
6052

6153
```scala
62-
def isEvenMethod(i: Int) = i % 2 == 0 // a method
63-
val isEvenFunction = (i: Int) => i % 2 == 0 // a function
54+
def times10(i: Int) = i * 10
55+
List(1, 2, 3).map(x => times10(x)) // eta expansion of `.map(times10)`
6456
```
6557

6658
{% endtab %}
6759
{% endtabs %}
6860

69-
The function truly is an object, so you can use it just like any other variable, such as putting it in a list:
61+
> For the curious, the term eta-expansion has its origins in the [Lambda Calculus](https://en.wikipedia.org/wiki/Lambda_calculus).
62+
63+
## When does eta-expansion happen?
7064

71-
{% tabs fun_4 %}
72-
{% tab 'Scala 2 and 3' for=fun_4 %}
65+
Automatic eta-expansion is a desugaring that is context-dependent (i.e. the expansion conditionally activates, depending on the surrounding code of the method reference.)
66+
67+
{% tabs fun_5 class=tabs-scala-version %}
68+
{% tab 'Scala 2' %}
7369

70+
In Scala 2 eta-expansion only occurs automatically when the expected type is a function type.
71+
For example, the following will fail:
7472
```scala
75-
val functions = List(isEvenFunction)
73+
def isLessThan(x: Int, y: Int): Boolean = x < y
74+
75+
val methods = List(isLessThan)
76+
// ^^^^^^^^^^
77+
// error: missing argument list for method isLessThan
78+
// Unapplied methods are only converted to functions when a function type is expected.
79+
// You can make this conversion explicit by writing `isLessThan _` or `isLessThan(_,_)` instead of `isLessThan`.
7680
```
7781

82+
See [below](#manual-eta-expansion) for how to solve this issue with manual eta-expansion.
7883
{% endtab %}
79-
{% endtabs %}
8084

81-
{% tabs fun_5 class=tabs-scala-version %}
82-
{% tab 'Scala 2' for=fun_5 %}
85+
{% tab 'Scala 3' %}
86+
87+
New to Scala 3, method references can be used everywhere as a value, they will be automatically converted to a function object with a matching type. e.g.
8388

8489
```scala
85-
// this example shows the Scala 2 error message
86-
val methods = List(isEvenMethod)
87-
^
88-
error: missing argument list for method isEvenMethod
89-
Unapplied methods are only converted to functions when a function type is expected.
90-
You can make this conversion explicit by writing `isEvenMethod _` or `isEvenMethod(_)` instead of `isEvenMethod`.
91-
```
90+
def isLessThan(x: Int, y: Int): Boolean = x < y
9291

93-
Conversely, a method technically isn’t an object, so in Scala 2 you couldn’t put a method in a `List`, at least not directly, as shown in this example:
92+
val methods = List(isLessThan) // works
93+
```
9494

9595
{% endtab %}
96+
{% endtabs %}
97+
98+
## Manual eta-expansion
9699

97-
{% tab 'Scala 3' for=fun_5 %}
100+
You can always manually eta-expand a method to a function value, here are some examples how:
101+
102+
{% tabs fun_6 class=tabs-scala-version %}
103+
{% tab 'Scala 2' %}
98104

99105
```scala
100-
val functions = List(isEvenFunction) // works
101-
val methods = List(isEvenMethod) // works
106+
val methodsA = List(isLessThan _) // way 1: expand all parameters
107+
val methodsB = List(isLessThan(_, _)) // way 2: wildcard application
108+
val methodsC = List((x, y) => isLessThan(x, y)) // way 3: anonymous function
102109
```
103110

104-
The important part for Scala 3 is that the Eta Expansion technology is improved, so now when you attempt to use a method as a variable, it just works---you don’t have to handle the manual conversion yourself.
111+
{% endtab %}
112+
113+
{% tab 'Scala 3' %}
114+
115+
```scala
116+
val methodsA = List(isLessThan(_, _)) // way 1: wildcard application
117+
val methodsB = List((x, y) => isLessThan(x, y)) // way 2: anonymous function
118+
```
105119

106120
{% endtab %}
107121
{% endtabs %}
108122

123+
## Summary
124+
109125
For the purpose of this introductory book, the important things to know are:
110126

111-
- Eta Expansion is the Scala technology that lets you use methods just like functions
112-
- The technology has been improved in Scala 3 to be almost completely seamless
127+
- eta-expansion is a helpful desugaring that lets you use methods just like functions,
128+
- the automatic eta-expansion been improved in Scala 3 to be almost completely seamless.
113129

114130
For more details on how this works, see the [Eta Expansion page][eta_expansion] in the Reference documentation.
115131

0 commit comments

Comments
 (0)