Skip to content

Commit c65a6d6

Browse files
authored
Restrict to cases where there is an expected type
1 parent 7a7f71d commit c65a6d6

File tree

1 file changed

+7
-80
lines changed

1 file changed

+7
-80
lines changed

content/polymorphic-eta-expansion.md

Lines changed: 7 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,11 @@ permalink: /sips/:title.html
2121
- For returning readers, a quick reminder of what the proposal is about. -->
2222

2323
We propose to extend eta-expansion to polymorphic methods.
24-
This means automatically transforming polymorphic methods into corresponding polymorphic functions, for example:
24+
This means automatically transforming polymorphic methods into corresponding polymorphic functions when required, for example:
2525

2626
~~~ scala
2727
def f1[A](x: A): A = ???
2828
val v1_1: [B] => B => B = f1 // f1 becomes [B'] => (y: B') => f1[B'](y)
29-
val v1_2 = f1 // f1 becomes [A'] => (x': A') => f1[A'](x')
3029
~~~
3130

3231
Returning readers, for a quick glance at a wide array of examples illustrated like the above, go to [High-level overview](#high-level-overview).
@@ -201,16 +200,13 @@ In the following `id'` means a copy of `id` with a fresh name, and `//reminder:`
201200
~~~ scala
202201
def f1[A](x: A): A = ???
203202
val v1_1: [B] => B => B = f1 // f1 becomes [B'] => (y: B') => f1[B'](y)
204-
val v1_2 = f1 // f1 becomes [A'] => (x': A') => f1[A'](x')
205203

206204
def f2[A]: A => A = ???
207205
val v2_1: [B] => B => B = f2 // f2 becomes [B'] => (y: B') => f2[B'](y)
208-
val v2_2 = f2 // f2 becomes f2[Any]
209206

210207
type F[C] = C => C
211208
def f3[A]: F[A] = ???
212209
val v3_1: [B] => B => B = f3 // f3 becomes [B'] => (y: B') => f3[B'](y)
213-
val v3_1: = f3 // f3 becomes f3[Any]
214210

215211
//reminder:
216212
val vErr: [B] => B = ??? // error: polymorphic function types must have a value parameter
@@ -222,22 +218,18 @@ extension (x: Int)
222218
def extf1[A](x: A): A = ???
223219

224220
val extv1_1: [B] => B => B = extf1(4) // extf1(4) becomes [B'] => (y: B') => extf1(4)[B'](y)
225-
val extv1_2 = extf1(4) // extf1(4) becomes [A'] => (x': A') => extf1(4)[A'](x')
226221

227222
val extv1_3: Int => [B] => B => B = extf1 // extf1 becomes (i: Int) => [B'] => (y: B') => extf1(i)[B'](y)
228-
val extv1_4 = extf1 // extf1 becomes (i: Int) => [A'] => (x': A') => extf1(i)[A'](x')
229223
~~~
230224

231225
#### Implicit parameters:
232226
~~~ scala
233227
def uf1[A](using x: A): A = ???
234228
val vuf1_1: [B] => B ?=> B = uf1 // uf1 becomes [B'] => (y: B') ?=> uf1[B']
235-
val vuf1_1: = uf1 // uf1 becomes uf1[X](using x) where X and x are given by type and term inference
236229

237230

238231
def uf2[A]: A = ???
239232
val vuf2: [B] => B ?=> B = uf2 // uf2 becomes [B'] => (y: B') ?=> uf2[B']
240-
val vuf2: = uf2 // uf2 becomes uf2[X] where X is given by type inference
241233

242234
//reminder:
243235
val get: (String) ?=> Int = 22 // 22 becomes (s: String) ?=> 22
@@ -270,9 +262,9 @@ val voly: Int => [T] => T => T = poly
270262

271263
As this feature only provides a shortcut to express already definable objects, the only impacted area is the type system.
272264

273-
When typing a polymorphic method `m` there are three cases to consider:
265+
When typing a polymorphic method `m` there are two cases to consider:
274266

275-
#### With expected type
267+
#### Polymorphic expected type
276268
If the expected type is a polymorphic function taking `[T_1 <: U_1 >: L_1, ..., T_n <: U_n >: L_n]` as type parameters, `(A_1, ..., A_k)` as term parameters and returning `R`, we proceed as follows:
277269

278270
Note: Polymorphic functions always take term parameters (but `k` can equal zero if the clause is explicit: `[T] => () => T`).
@@ -297,7 +289,7 @@ Note: Polymorphic functions always take term parameters (but `k` can equal zero
297289
* 1. If it succeeds, the above is the created tree.
298290
* 2. If it fails, go to [Default](#Default).
299291

300-
At 3.2. if the cause of the error is such that [Default](#Default) will never succeed, we might return that error directly, this is at the discretion of the implementation, to make errors as clear as possible.
292+
At 3.ii. if the cause of the error is such that [Non-polymorphic expected type](#non-polymorphic-expected-type) will never succeed, we might return that error directly, this is at the discretion of the implementation, to make errors as clear as possible.
301293

302294
Note: Type checking will be in charge of overloading resolution, as well as term inference, so the following will work:
303295
~~~ scala
@@ -327,43 +319,7 @@ val voo: [T] => T => [U] => U => (T, U) = foo
327319
// [T'] => (t: T') => [U'] => (u: U') => foo[T'](t)[U'](u)
328320
~~~
329321

330-
#### Without expected type
331-
332-
<!-- TODO: Think more about overloading
333-
334-
If there is no expected type, let `ms` be all the overloaded variants of `m` which take at least one explicit parameter clause.
335-
336-
If `ms` contains no variants, go to [Default](#Default).
337-
If it contains more than one variant, throw an overloading error.
338-
339-
If `ms` contains exactly one variant, then it is expanded as follows:
340-
-->
341-
342-
`m` has to contain at least one explicit term clause before the return type or the next type clause.
343-
<details>
344-
<summary>Note</summary>
345-
Since the only way to create multiple type clause is with extension methods, and since they force an explicit clause, this condition is currently equivalent to "`m` contains explicit term parameters".
346-
The stricter wording was chosen to already accommodate [SIP-47 - Clause Interleaving](https://github.com/scala/improvement-proposals/pull/47).
347-
</details>
348-
349-
If `m` doesn't satisfy the above condition, go to [Default](#Default), otherwise:
350-
351-
Assuming in all generality that `m` takes `[T_1 <: U_1 >: L_1, ..., T_n <: U_n >: L_n]` as type parameters and `(A_1, ..., A_k)` as the first clause of (explicit or implicit) term parameters, then `m` is expanded as follows:
352-
353-
1. Copies of `T_i`s are created, and replaced in `U_i`s, `L_i`s, `A_i`s and `R`, noted respectively `T'_i`, `U'_i`, `L'_i` and `A'_i`.
354-
355-
2. `m` is replaced by the following:
356-
~~~ scala
357-
[T'_1 <: U'_1 >: L'_1, ... , T'_n <: U'_n >: L'_n]
358-
=> (a_1: A'_1 ..., a_k: A'_k)
359-
=> m[T'_1, ..., T'_n](a_1, ..., a_k)
360-
~~~
361-
362-
3. `m[T'_1, ..., T'_n](a_1, ..., a_k)` is type-checked (with no expected type).
363-
364-
Note: Again, type checking takes care of term inference, and there is no overloading resolution necessary, as there is only one option by assumption.
365-
366-
#### Default
322+
#### Non-polymorphic expected type
367323

368324
No polymorphic eta-expansion is performed, this corresponds to the old behaviour, written here as a reminder:
369325

@@ -394,7 +350,7 @@ As this proposal never generates code that couldn't have been written by hand be
394350

395351
#### Source
396352

397-
This proposal conserves source compatibility when a non-polymorphic type is present, since by definition the behaviour is the same.
353+
This proposal conserves source compatibility when a non-polymorphic expected type is present, or when there is no expected type, since by definition the behaviour is the same.
398354

399355
In the case the expected type is polymorphic, either the code did not compile before, or there was an implicit conversion from the inferred monomorphic function to the expected polymorphic function. In the latter case, source compatibility is broken, since polymorphic eta-expansion will apply before search for implicit conversions, for example:
400356

@@ -410,24 +366,11 @@ val function: [T] => T => T = method
410366
// now: method is eta-expanded to [T] => T => T (conv is not called)
411367
```
412368

413-
When there is no expected type, this proposal breaks source compatibility, in this regard it is essentially a change in type inference, and therefore has all the usual repercussions of changing type inference, for example:
414-
415-
```scala
416-
def method[T](x: T): T = x
417-
val function = method // was Any => Any is now [T] => T => T
418-
val y = function(5) // was Any is now Int
419-
420-
def lookFor[T](x: T)(using u: T): T = u
421-
lookFor(method) // was searching for Any => Any, is now searching for [T] => T => T
422-
// This change in search can find different instances, and thus potentially wildly different behaviour !
423-
```
424-
425-
While these examples might seem damming, this is no different than any other change to type inference.
426-
427369
### Restrictions
428370

429371
Not included in this proposal are:
430372

373+
* Applying polymorphic eta-expansion when there is no return type
431374
* Expanding `[T] => T => T` to `[T] => T => Id[T]` to make `tuple.map(identity)` work (might work out of the box anyways, but not guaranteed)
432375
* Expanding `x => x` to `[T] => (x: T) => x` if necessary (and generalizations)
433376
* Expanding `_` to `[T] => (x: T) => x` if necessary (and generalizations)
@@ -447,22 +390,6 @@ If you think of anything else that is worth discussing about the proposal, this
447390

448391
<!-- If some design aspects are not settled yet, this section can present the open questions, with possible alternatives. By the time the proposal is accepted, all the open questions will have to be resolved. -->
449392

450-
#### Overloaded methods and no expected type
451-
452-
In the case where there is no expected type, there is nothing to guide overloading resolution, and it becomes complicated to find a good metric for the "best" variant, for example where we compare signatures in pairs:
453-
454-
~~~ scala
455-
(A)(B): R
456-
(A)(using C): R // currently more specific
457-
458-
(A)(B): R // currently more specific
459-
(A)[T](T): R
460-
461-
(A)(B): R // currently more specific
462-
(A)[T](using T): R // intuitively more specific ?
463-
~~~
464-
465-
As we have not yet been able to find a good universal and intuitive heuristic, we propose to let this question be settled on during the implementation phase, as the ease of implementation can help to choose between equivalently good imperfect metrics.
466393
<!--
467394
## Alternatives
468395
-->

0 commit comments

Comments
 (0)