为什么 SAM 规则不适用于无参数方法
Why SAM rule doesn't work on parameterless method
// ok
val sam0: MySamWithEmptyParameter = () => 100
// doesn't work
// val sam1: MySamWithParameterless = () => 100
trait MySamWithEmptyParameter {
def receive(): Int
}
trait MySamWithParameterless {
def receive: Int
}
为什么 sam1 无法覆盖 receive 方法? scalac 将两个特征编译为相同的代码。
abstract trait TestSAM$MySamWithEmptyParameter extends Object {
def receive(): Int
};
abstract trait TestSAM$MySamWithParameterless extends Object {
def receive(): Int
};
SI-10555 正是在谈论这个。这是一个简单的设计决定,只支持一个显式的空参数列表,即使两者编译成一个空参数列表也是如此。
The relevant part of the Specification says(强调我的):
- the method m must have a single argument list;
这确实有点尴尬,因为 eta 扩展确实适用于参数列表为空的方法。
编辑
联系了 Lightbend 的人。以下是 Scala 团队负责人 Adrian Moors 的回复:
The original reason was to keep the spec simple, but perhaps we should revisit. I agree it’s surprising that it works for def a(): Int
, but not in your example.
Internally, methods that don’t define an argument list at all, and those that do (even if empty) are treated differently.
This has led to confusion/bugs before — to name just one: https://github.com/scala/scala-dev/issues/284.
In 2.13, we’re reworking eta-expansion (it will apply more aggressively, but ()-insertion will happen first). We’ve been back and forth on this, but the current thinking is:
- 0-ary methods are treated specially: if the expected type is sam-equivalent to Function0, we eta-expand; otherwise,
()
is inserted (in dotty, you are required to write the () explicitly, unless the method is java-defined) — I’m still not sure about whether we should ever eta-expand here
- for all other arities, a method reference is eta-expanded regardless of the expected type (if there’s no type mismatch, this could hide errors when you refactor a method to take arguments, but forget to apply them everywhere. However, since functions are first-class values, it should be easy to construct them by simplify referring to a method value).
The upshot is that we can deprecate method value syntax (m _
), since it’s subsumed by simply writing m
. (Note that this is distinct from placeholder syntax, as in m(, _)
.)
(See also the thread around this comment: https://github.com/lampepfl/dotty/issues/2570#issuecomment-306202339)
// ok
val sam0: MySamWithEmptyParameter = () => 100
// doesn't work
// val sam1: MySamWithParameterless = () => 100
trait MySamWithEmptyParameter {
def receive(): Int
}
trait MySamWithParameterless {
def receive: Int
}
为什么 sam1 无法覆盖 receive 方法? scalac 将两个特征编译为相同的代码。
abstract trait TestSAM$MySamWithEmptyParameter extends Object {
def receive(): Int
};
abstract trait TestSAM$MySamWithParameterless extends Object {
def receive(): Int
};
SI-10555 正是在谈论这个。这是一个简单的设计决定,只支持一个显式的空参数列表,即使两者编译成一个空参数列表也是如此。
The relevant part of the Specification says(强调我的):
- the method m must have a single argument list;
这确实有点尴尬,因为 eta 扩展确实适用于参数列表为空的方法。
编辑
联系了 Lightbend 的人。以下是 Scala 团队负责人 Adrian Moors 的回复:
The original reason was to keep the spec simple, but perhaps we should revisit. I agree it’s surprising that it works for
def a(): Int
, but not in your example.Internally, methods that don’t define an argument list at all, and those that do (even if empty) are treated differently. This has led to confusion/bugs before — to name just one: https://github.com/scala/scala-dev/issues/284.
In 2.13, we’re reworking eta-expansion (it will apply more aggressively, but ()-insertion will happen first). We’ve been back and forth on this, but the current thinking is:
- 0-ary methods are treated specially: if the expected type is sam-equivalent to Function0, we eta-expand; otherwise,
()
is inserted (in dotty, you are required to write the () explicitly, unless the method is java-defined) — I’m still not sure about whether we should ever eta-expand here- for all other arities, a method reference is eta-expanded regardless of the expected type (if there’s no type mismatch, this could hide errors when you refactor a method to take arguments, but forget to apply them everywhere. However, since functions are first-class values, it should be easy to construct them by simplify referring to a method value).
The upshot is that we can deprecate method value syntax (
m _
), since it’s subsumed by simply writingm
. (Note that this is distinct from placeholder syntax, as inm(, _)
.) (See also the thread around this comment: https://github.com/lampepfl/dotty/issues/2570#issuecomment-306202339)