I ran into a problem concerning how scala types eta-expansions of methods. Here is a minimal example:
class MethodHolder:
def method(x: Int): 4 = 4
val a = new MethodHolder
val b = a.method
Scala types b by default as val b: Int => Int, whilst manually giving the more specific type val b: Int => 4 type checks just fine. Is this by design? I’m doing some transparent inline things where it would be nice if scala gave the more specifc type.
scala> def m(i: Int): 42 = 42
[[syntax trees at end of typer]] // rs$line$2
package <empty> {
final lazy module val rs$line$2: rs$line$2 = new rs$line$2()
final module class rs$line$2() extends Object() { this: rs$line$2.type =>
def m(i: Int): 42 = 42
}
}
def m(i: Int): 42
scala> val f: Int => Unit = m
[[syntax trees at end of typer]] // rs$line$3
package <empty> {
final lazy module val rs$line$3: rs$line$3 = new rs$line$3()
final module class rs$line$3() extends Object() { this: rs$line$3.type =>
val f: Int => Unit = (i: Int) =>
{
m(i)
()
}
}
}
val f: Int => Unit = Lambda/0x0000000066498000@4b1c6936
which is because eta-expansion gives you the function and its body or result has the expected type Unit, so the result is discarded. Similarly for Int => 42. The expected type matters, but maybe there is a SIP – I didn’t realize the precise type effort had stalled, but now I’m curious to read the history. That was SIP-48, it was even a pretty good SIP number and was also pull request number 48.
The complaint on the other ticket was about this diagnostic:
scala> val f: Int => 27 = m
-- [E007] Type Mismatch Error: -------------------------------------------------
1 |val f: Int => 27 = m
| ^
| Found: (42 : Int)
| Required: (27 : Int)
Separately, I just had a PR that rewrites final val to inline def, more or less, because Java enums are not constant, so concurrent.duration.DAYS can’t be used in annotations. The spec is that final val of a constant means inline the RHS, and that works only for constants via folding and not inlining. (A def is not constant, even if its type is a literal type.)
For your use case, even with everything inline transparent, the inferred result of the function is widened (in the absence of an expected type), as you said.
scala> inline transparent def m(inline i: Int): 42 = 42
def m(i: Int): 42
scala> inline transparent def f = m
def f: Int => Int
scala> val x: 42 = f(27)
-- [E007] Type Mismatch Error: -------------------------------------------------
1 |val x: 42 = f(27)
| ^^^^^
| Found: Int
| Required: (42 : Int)
|
| longer explanation available when compiling with `-explain`
1 error found
scala> def f: Int => 42 = m
def f: Int => 42
scala> val x: 42 = f(27)
val x: 42 = 42