Skip to content

Commit 7a7f71d

Browse files
committed
Add compatibility edge-case with conversions
1 parent d5b7726 commit 7a7f71d

File tree

1 file changed

+22
-10
lines changed

1 file changed

+22
-10
lines changed

content/polymorphic-eta-expansion.md

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -390,27 +390,39 @@ This section should also argue to what extent backward source compatibility is p
390390

391391
#### Binary and TASTy
392392

393-
As this proposal never generates code that couldn't have been written before, these changes are binary and TASTy compatible.
393+
As this proposal never generates code that couldn't have been written by hand before, these changes are binary and TASTy compatible.
394394

395395
#### Source
396396

397-
This proposal conserves source compatibility when an expected type is present, since either the behaviour is the same, or the code did not compile before.
397+
This proposal conserves source compatibility when a non-polymorphic type is present, since by definition the behaviour is the same.
398398

399-
This proposal breaks source compatibility only when there is no expected type, in this regard it is essentially a change in type inference, and therefore has all the usual repercussions of changing type inference, for example:
399+
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:
400400

401401
```scala
402-
def foo[T](x: T): T = x
403-
val voo = foo // was Any => Any is now [T] => T => T
404-
val y = voo(5) // was Any is now Int
402+
import scala.language.implicitConversions
403+
404+
given conv: Conversion[Any => Any, [T] => T => T] = f => ([T] => (x: T) => x)
405+
406+
def method[T](x: T): T = x
407+
408+
val function: [T] => T => T = method
409+
// before: method is eta-expanded to Any => Any, and then converted using conv to [T] => T => T
410+
// now: method is eta-expanded to [T] => T => T (conv is not called)
411+
```
412+
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
405419

406420
def lookFor[T](x: T)(using u: T): T = u
407-
lookFor(foo) // was searching for Any => Any, is now searching for [T] => T => T
421+
lookFor(method) // was searching for Any => Any, is now searching for [T] => T => T
408422
// This change in search can find different instances, and thus potentially wildly different behaviour !
409423
```
410424

411-
While these examples might seem damming, this is the case every time we change type inference.
412-
413-
**Note:** All of this disappears if we change `val voo` to `val voo: Any => Any`, this is the reason it is recommended for library authors to always provide explicit types for all public members.
425+
While these examples might seem damming, this is no different than any other change to type inference.
414426

415427
### Restrictions
416428

0 commit comments

Comments
 (0)