From 11dae6faaa08583a0e7a895987f5afba19e89477 Mon Sep 17 00:00:00 2001 From: Aggelos Biboudis Date: Thu, 1 Nov 2018 15:24:01 +0100 Subject: [PATCH 1/2] Update automatic eta-expansion spec --- .../reference/changed/eta-expansion-spec.md | 105 ++++++++++++++++++ docs/docs/reference/changed/eta-expansion.md | 45 ++------ 2 files changed, 116 insertions(+), 34 deletions(-) create mode 100644 docs/docs/reference/changed/eta-expansion-spec.md diff --git a/docs/docs/reference/changed/eta-expansion-spec.md b/docs/docs/reference/changed/eta-expansion-spec.md new file mode 100644 index 000000000000..fc17c622b912 --- /dev/null +++ b/docs/docs/reference/changed/eta-expansion-spec.md @@ -0,0 +1,105 @@ +--- +layout: doc-page +title: "Automatic Eta Expansion" +--- + +### Motivation + +Scala maintains a convenient distinction between _methods_ and _functions_. +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. +These two mechanisms are bridged in Scala by a mechanism called _eta-expansion_ in literature also called eta-abstraction). +According to this, methods can be turned into functions. +The intuition behind this, is that if we have a function `f(x)` and we need to pass it around +we can either pass its name `f` or a function `x => f(x)` which expresses the idea that two functions +are equivalent if and only if they give the same result for all arguments. + +Consequently, the essense of eta-expansion is captured in the following snippet. +Imagine that the `val` is generated by the compiler, when the programmer writes ```f = m```. +The right-hand side is not a function so the compiler performs _automatic eta-expansion_: + +```scala +def m(x: Int, y: String) = ??? +val f = m // generates val f = (x: Int, y: String) => m(x, y) +``` + +In Scala, previously, a method reference `m` was converted to a function value +only if the expected type was a function type. If that was not the +case, one had to write `m _` to force the conversion. + +For methods with one or more parameters like in the example above, this restriction has now been +dropped. The syntax `m _` is no longer needed and will be deprecated in the +future. + +## Automatic eta-expansion and partial application +In the following example `m` can be partially applied to the first two parameters. +Assignining `m` to `f1` will automatically eta-expand. + +```scala +def m(x: Boolean, y: String)(z: Int): List[Int] +val f1 = m +val f2 = m(true, "abc") +``` + +This creates two function values: + +```scala +f1: (Boolean, String) => Int => List[Int] +f2: Int => List[Int] +``` + +## Automatic eta-expansion and nullary methods + +Automatic eta expansion does not apply to "nullary" methods that take an empty parameter list. Given + +```scala +def next(): T +``` + +A simple reference to `next` does not auto-convert to a function. +One has to write explicitly `() => next()` to achieve that +Once again since the `_` is going to be deprecated it's better to write it this way +rather than `next _`. + +The reason for excluding nullary methods from automatic eta expansion +is that Scala implicitly inserts the `()` argument, which would +conflict with eta expansion. Automatic `()` insertion is +[limited](../dropped/auto-apply.md) in Dotty, but the fundamental ambiguity +remains. + +## Automatic eta-expansion and implicit parameter lists + +Methods with implicit parameter lists will always get applied to implicit arguments. + +```scala +def foo(x: Int)(implicit p: Double): Float = ??? +implicit val bla: Double = 1.0 + +val bar = foo // val bar: Int => Float = ... +``` + +## Automatic Eta-Expansion and implicit function types + +Methods with implicit parameter lists can be assigned to a value with an implicit function type +only by using the expected type explicitly. + +```scala +def foo(x: Int)(implicit p: Double): Float = ??? +val bar: implicit Double => Float = foo(3) // val bar: implicit Double => Float = ... +``` + +## Rules + +- If `m` has one or more parameters, we always eta-expand +- If `m` is nullary (i.e. has type `()R`): + 1. If the expected type is of the form `() => T`, we eta expand. + 2. If m is defined by Java, or overrides a Java defined method, we insert `()`. + 3. Otherwise we issue an error of the form: +Unapplied nullary methods are only converted to functions when a function type is expected. +You need to either apply the method to `()`, or convert it to a function with `() => m()`. + +The syntax `m _` is deprecated. + +### Reference + +For more info, see [PR #2701](https://github.com/lampepfl/dotty/pull/2701). + diff --git a/docs/docs/reference/changed/eta-expansion.md b/docs/docs/reference/changed/eta-expansion.md index 9035f951677a..1ed45bc277ca 100644 --- a/docs/docs/reference/changed/eta-expansion.md +++ b/docs/docs/reference/changed/eta-expansion.md @@ -3,42 +3,19 @@ layout: doc-page title: "Automatic Eta Expansion" --- -Previously, a method reference `m` was converted to a function value -only if the expected type was a function type. If that was not the -case, one had to write `m _` to force the conversion (which is called -eta-expansion). +The conversion of _methods_ into _functions_ has been improved and happens automatically for methods with one or more parameters. -For methods with one or more parameters, this restriction has now been -dropped. Example: - - def m(x: Boolean, y: String)(z: Int): List[Int] - val f1 = m - val f2 = m(true, "abc") +```scala +def m(x: Boolean, y: String)(z: Int): List[Int] +val f1 = m +val f2 = m(true, "abc") +``` This creates two function values: +```scala +f1: (Boolean, String) => Int => List[Int] +f2: Int => List[Int] +``` - f1: (Boolean, String) => Int => List[Int] - f2: Int => List[Int] - -The syntax `m _` is no longer needed and will be deprecated in the -future. - -Automatic eta expansion does not apply to "nullary" methods that take an empty parameter list. Given - - def next(): T - -, a simple reference to `next` does not auto-convert to a -function. One has to write explicitly `() => next()` to achieve that -(it's better to write it this way rather than `next _` because the latter -will be deprecated). - -The reason for excluding nullary methods from automatic eta expansion -is that Scala implicitly inserts the `()` argument, which would -conflict with eta expansion. Automatic `()` insertion is -[limited](../dropped/auto-apply.md) in Dotty, but the fundamental ambiguity -remains. - -### Reference - -For more info, see [PR #2701](https://github.com/lampepfl/dotty/pull/2701). +[More details](eta-expansion-spec.html) From c86e69d7d97b270106ef85b336763a07768440b0 Mon Sep 17 00:00:00 2001 From: Aggelos Biboudis Date: Wed, 5 Dec 2018 14:01:00 +0100 Subject: [PATCH 2/2] Address comments --- .../reference/changed/eta-expansion-spec.md | 21 +------------------ docs/docs/reference/changed/eta-expansion.md | 21 +++++++++++++++++++ 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/docs/docs/reference/changed/eta-expansion-spec.md b/docs/docs/reference/changed/eta-expansion-spec.md index fc17c622b912..cfbab5c6c330 100644 --- a/docs/docs/reference/changed/eta-expansion-spec.md +++ b/docs/docs/reference/changed/eta-expansion-spec.md @@ -1,6 +1,6 @@ --- layout: doc-page -title: "Automatic Eta Expansion" +title: "Automatic Eta Expansion - More Details" --- ### Motivation @@ -47,25 +47,6 @@ f1: (Boolean, String) => Int => List[Int] f2: Int => List[Int] ``` -## Automatic eta-expansion and nullary methods - -Automatic eta expansion does not apply to "nullary" methods that take an empty parameter list. Given - -```scala -def next(): T -``` - -A simple reference to `next` does not auto-convert to a function. -One has to write explicitly `() => next()` to achieve that -Once again since the `_` is going to be deprecated it's better to write it this way -rather than `next _`. - -The reason for excluding nullary methods from automatic eta expansion -is that Scala implicitly inserts the `()` argument, which would -conflict with eta expansion. Automatic `()` insertion is -[limited](../dropped/auto-apply.md) in Dotty, but the fundamental ambiguity -remains. - ## Automatic eta-expansion and implicit parameter lists Methods with implicit parameter lists will always get applied to implicit arguments. diff --git a/docs/docs/reference/changed/eta-expansion.md b/docs/docs/reference/changed/eta-expansion.md index 1ed45bc277ca..4fa5f9ae0a99 100644 --- a/docs/docs/reference/changed/eta-expansion.md +++ b/docs/docs/reference/changed/eta-expansion.md @@ -17,5 +17,26 @@ f1: (Boolean, String) => Int => List[Int] f2: Int => List[Int] ``` +The syntax `m _` is no longer needed and will be deprecated in the future. + +## Automatic eta-expansion and nullary methods + +Automatic eta expansion does not apply to "nullary" methods that take an empty parameter list. + +```scala +def next(): T +``` + +Given a simple reference to `next` does not auto-convert to a function. +One has to write explicitly `() => next()` to achieve that +Once again since the `_` is going to be deprecated it's better to write it this way +rather than `next _`. + +The reason for excluding nullary methods from automatic eta expansion +is that Scala implicitly inserts the `()` argument, which would +conflict with eta expansion. Automatic `()` insertion is +[limited](../dropped/auto-apply.md) in Dotty, but the fundamental ambiguity +remains. + [More details](eta-expansion-spec.html)