我的问题是关于 Odersky 的第 3.5 课(不是作业!):
19:15 的练习问:“什么是类型:if (true) 1 else false
”
在他的解释中,Odersky 认为类型是AnyVal
因为它是Int
和Boolean
(两个条件分支的类型)最具体的超类型。
我问自己为什么类型检查器不够聪明,无法看到只有第一个分支实际上是相关的,因此可以推断出类型Int
?
我的问题是关于 Odersky 的第 3.5 课(不是作业!):
19:15 的练习问:“什么是类型:if (true) 1 else false
”
在他的解释中,Odersky 认为类型是AnyVal
因为它是Int
和Boolean
(两个条件分支的类型)最具体的超类型。
我问自己为什么类型检查器不够聪明,无法看到只有第一个分支实际上是相关的,因此可以推断出类型Int
?
这与为什么以下内容无法在 Java 中编译类似的问题:
throw new Foo();
return 42; //unreachable statement
但这会编译:
if(true)
throw new Foo();
return 42;
基本上编译器必须在某处停止。尤其是 Scala 编译器,它已经很慢了。一旦它识别true
出总是通过条件,人们可能会问:what about: 1 == 1
,2 * 21 == 42
甚至x == 7
where x
is val
。
很难画一条线,所以编译器只是假设每个都if
可以有两个分支。IDE 或代码验证工具有责任发现这些不可能或可能不正确的表达式。
我认为您不希望Int
- 推断类型不应依赖于优化,而应依赖于开发人员编写的文本。否则,很难预测你会得到什么——当然,不是在这种情况下,但它显然比大多数人更明显。
因为类型检查器不对程序的执行做出任何假设。它不会尝试预测 if 语句的哪个分支将执行。在一般情况下,这是无法确定的,而且这种特定情况在实践中不会经常发生,以至于使任何此类预测都值得。
做这样的预测也会使打字规则的逻辑复杂化。如果 if 语句的类型是 Int 并且其中一个分支的类型不是 Int 的子类型,那肯定会与人们的期望相矛盾——即使该分支永远不会被执行。在某种程度上,就像类型检查器不会抱怨它知道不会执行的分支中的类型错误一样——没有人想要那样。
这让我开始思考,因为您可以将true
定义的位置向上移动。在某些时候,编译器需要遵循很多路径才能得出最佳猜测。
这总是正确的
if (true) 1 else false
这也是,但在更高的水平
val presetBoolean = true
if (presetBoolean) 1 else false
这也是如此,但是通过一个评估的表达式
val presetBoolean = (1 == 1)
if (presetBoolean) 1 else false
这也是常量,但在这种情况下,它将引用传递给另一个对象(并且是我程序中的唯一实例
case object CallWithPreset {
def apply(presetBoolean:Boolean) {
if(presetBoolean) 1 else false
}
}
CallWithPreset(true)
每次推理都有编译时间形式的开销。因此,虽然我认为在微不足道的情况下可能很容易,但在其他情况下可能不会。但是,如果您允许某些情况而不是所有情况,则可能会导致每种情况都有不同的推断,并且可能会造成混淆。
例如,如果这有效:
val myType:Int = if(true) 1 else false
但这给出了编译错误:
val presetBoolean = true
val myType:Int = if (presetBoolean) 1 else false