2015-12-07 129 views
3

我有一些Scala类型推理的麻烦。 在下面的工作表示例中,我定义了一个将任意值映射到返回单位值的函数的映射。斯卡拉奇怪的类型推断

有趣的是,当我尝试仅使用一行代码定义相同的地图时,它不起作用,因为'bar'函数的返回类型突然更改为Any而不是Unit。

type UnitFun = (Any) => Unit 

val foo = "foo" 
val bar = (a: Any) => System.out.println("bar") 

val map: Map[Any, UnitFun] = Map().withDefaultValue(((a: Any) => Unit)) 
val doesCompile: Map[Any, UnitFun] = map + (foo -> bar) 

val doesNotCompile: Map[Any, UnitFun] = Map().withDefaultValue(((a: Any) => Unit)) + (foo -> bar) 

我使用IDEA14作为IDE使用Scala 2.11.6

在我看来,这样是Scala编译器的特性/错误,还是我失去了一些东西?

顺便说一句,我刚刚注意到,当我使用“doesNotCompile”酒吧'作为默认值是这样的:

val doesCompileNow: Map[Any, UnitFun] = Map().withDefaultValue(bar) + (foo -> bar) 

突然似乎工作,我很困惑,现在。 :d

编辑1: @Mikolak

在这种情况下,如何将下面的代码工作? :)

val a: Any => Unit = (a: Any) => Unit 
val b: Any => Unit = (a: Any) =>() 

不应该两个表达式都是不同的类型?或者是否存在一些隐式类型转换?

+0

添加了关于您的编辑的解释。请注意,这是一个与原始完全正交的现象。 –

+0

已经看到它了,我认为从** Unit.type **到** Any **(根据intellij输出)的隐式转换是导致我在这一个轨道上偏离的原因。我认为这只是另一个不切实际的转换。 :) – pmkrefeld

+0

然后添加一件事:**不信任** IntelliJ的语法检查。在逐渐变好的同时,它仍然倾向于输出假阳性和假阴性。换句话说,当你遇到一个奇怪的错误时,你的第一步应该是运行'sbt compile'。 –

回答

8

原始编译错误

编译错误是因为该功能:

(a: Any) => Unit 

的类型是

Any => Unit.type 

类型:

Any => Unit 

换句话说,您将返回Unit,伴侣对象Unit类型。该伴侣对象的类型为Unit.type,这是不同的类型比Unit(这适用于Scala中的所有伴随对象)。

您需要的是实际返回Unit类型的值。 As outlined in the docs唯一的这样的值是()

因此,您的默认功能应该是:(a: Any) =>()


编辑:关于质询。

转换为单位

这里:

val a: Any => Unit = (a: Any) => Unit 

您明确地输入的表达,有Unit返回类型。通常情况下这将导致一个类型的错误,但是(如您怀疑)“幸运” 你触发one of the pre-defined implicit value conversions,具体如下:

价值丢弃

如果e的一些价值型和预期的类型为单位,通过将e嵌入到术语{e; ()}。

所以,这样的:

(a: Any) => Unit //return type is Unit.type 

变为:

(a: Any) => {Unit;();} //return type is Unit 

注意,按照定义,转换适用于任意值,从而例如val c: Unit = "c"产生相同的结果。