Skip to content

Commit b826bf4

Browse files
author
Aggelos Biboudis
authored
Merge pull request #5366 from biboudis/eta-expansion-spec
Update automatic eta-expansion spec
2 parents 2d19588 + c86e69d commit b826bf4

File tree

2 files changed

+107
-23
lines changed

2 files changed

+107
-23
lines changed
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
---
2+
layout: doc-page
3+
title: "Automatic Eta Expansion - More Details"
4+
---
5+
6+
### Motivation
7+
8+
Scala maintains a convenient distinction between _methods_ and _functions_.
9+
Methods are part of the definition of a class that can be invoked in objects while functions are complete objects themselves, making them first-class entities. For example they can be assigned in variables.
10+
These two mechanisms are bridged in Scala by a mechanism called _eta-expansion_ in literature also called eta-abstraction).
11+
According to this, methods can be turned into functions.
12+
The intuition behind this, is that if we have a function `f(x)` and we need to pass it around
13+
we can either pass its name `f` or a function `x => f(x)` which expresses the idea that two functions
14+
are equivalent if and only if they give the same result for all arguments.
15+
16+
Consequently, the essense of eta-expansion is captured in the following snippet.
17+
Imagine that the `val` is generated by the compiler, when the programmer writes ```f = m```.
18+
The right-hand side is not a function so the compiler performs _automatic eta-expansion_:
19+
20+
```scala
21+
def m(x: Int, y: String) = ???
22+
val f = m // generates val f = (x: Int, y: String) => m(x, y)
23+
```
24+
25+
In Scala, previously, a method reference `m` was converted to a function value
26+
only if the expected type was a function type. If that was not the
27+
case, one had to write `m _` to force the conversion.
28+
29+
For methods with one or more parameters like in the example above, this restriction has now been
30+
dropped. The syntax `m _` is no longer needed and will be deprecated in the
31+
future.
32+
33+
## Automatic eta-expansion and partial application
34+
In the following example `m` can be partially applied to the first two parameters.
35+
Assignining `m` to `f1` will automatically eta-expand.
36+
37+
```scala
38+
def m(x: Boolean, y: String)(z: Int): List[Int]
39+
val f1 = m
40+
val f2 = m(true, "abc")
41+
```
42+
43+
This creates two function values:
44+
45+
```scala
46+
f1: (Boolean, String) => Int => List[Int]
47+
f2: Int => List[Int]
48+
```
49+
50+
## Automatic eta-expansion and implicit parameter lists
51+
52+
Methods with implicit parameter lists will always get applied to implicit arguments.
53+
54+
```scala
55+
def foo(x: Int)(implicit p: Double): Float = ???
56+
implicit val bla: Double = 1.0
57+
58+
val bar = foo // val bar: Int => Float = ...
59+
```
60+
61+
## Automatic Eta-Expansion and implicit function types
62+
63+
Methods with implicit parameter lists can be assigned to a value with an implicit function type
64+
only by using the expected type explicitly.
65+
66+
```scala
67+
def foo(x: Int)(implicit p: Double): Float = ???
68+
val bar: implicit Double => Float = foo(3) // val bar: implicit Double => Float = ...
69+
```
70+
71+
## Rules
72+
73+
- If `m` has one or more parameters, we always eta-expand
74+
- If `m` is nullary (i.e. has type `()R`):
75+
1. If the expected type is of the form `() => T`, we eta expand.
76+
2. If m is defined by Java, or overrides a Java defined method, we insert `()`.
77+
3. Otherwise we issue an error of the form:
78+
Unapplied nullary methods are only converted to functions when a function type is expected.
79+
You need to either apply the method to `()`, or convert it to a function with `() => m()`.
80+
81+
The syntax `m _` is deprecated.
82+
83+
### Reference
84+
85+
For more info, see [PR #2701](https://github.com/lampepfl/dotty/pull/2701).
86+

docs/docs/reference/changed/eta-expansion.md

Lines changed: 21 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,42 +3,40 @@ layout: doc-page
33
title: "Automatic Eta Expansion"
44
---
55

6-
Previously, a method reference `m` was converted to a function value
7-
only if the expected type was a function type. If that was not the
8-
case, one had to write `m _` to force the conversion (which is called
9-
eta-expansion).
6+
The conversion of _methods_ into _functions_ has been improved and happens automatically for methods with one or more parameters.
107

11-
For methods with one or more parameters, this restriction has now been
12-
dropped. Example:
13-
14-
def m(x: Boolean, y: String)(z: Int): List[Int]
15-
val f1 = m
16-
val f2 = m(true, "abc")
8+
```scala
9+
def m(x: Boolean, y: String)(z: Int): List[Int]
10+
val f1 = m
11+
val f2 = m(true, "abc")
12+
```
1713

1814
This creates two function values:
15+
```scala
16+
f1: (Boolean, String) => Int => List[Int]
17+
f2: Int => List[Int]
18+
```
1919

20-
f1: (Boolean, String) => Int => List[Int]
21-
f2: Int => List[Int]
20+
The syntax `m _` is no longer needed and will be deprecated in the future.
2221

23-
The syntax `m _` is no longer needed and will be deprecated in the
24-
future.
22+
## Automatic eta-expansion and nullary methods
2523

26-
Automatic eta expansion does not apply to "nullary" methods that take an empty parameter list. Given
24+
Automatic eta expansion does not apply to "nullary" methods that take an empty parameter list.
2725

28-
def next(): T
26+
```scala
27+
def next(): T
28+
```
2929

30-
, a simple reference to `next` does not auto-convert to a
31-
function. One has to write explicitly `() => next()` to achieve that
32-
(it's better to write it this way rather than `next _` because the latter
33-
will be deprecated).
30+
Given a simple reference to `next` does not auto-convert to a function.
31+
One has to write explicitly `() => next()` to achieve that
32+
Once again since the `_` is going to be deprecated it's better to write it this way
33+
rather than `next _`.
3434

3535
The reason for excluding nullary methods from automatic eta expansion
3636
is that Scala implicitly inserts the `()` argument, which would
3737
conflict with eta expansion. Automatic `()` insertion is
3838
[limited](../dropped/auto-apply.md) in Dotty, but the fundamental ambiguity
3939
remains.
4040

41-
### Reference
42-
43-
For more info, see [PR #2701](https://github.com/lampepfl/dotty/pull/2701).
41+
[More details](eta-expansion-spec.html)
4442

0 commit comments

Comments
 (0)