4

在与 scala 2.10.0-M7 的以下会话中:

scala> trait A
defined trait A
scala> class B extends A
defined class B
scala> class C extends A
defined class C
scala> Some(0).fold(new B){_=>new C}
<console>:11: error: type mismatch;
 found   : C
 required: B
              Some(0).fold(new B){_=>new C}

我希望编译器找到共同的超类型(即 A)而不是抱怨。它是一般类型推断限制,还是 Option.fold 定义方式的结果?

谢谢你。

4

3 回答 3

10

Scalas 类型推理算法和方式如何Option.fold定义的结合的问题结果。

Scalas 类型推断从左到右工作,这意味着它从最左边的符号开始搜索表达式的可能类型。此外,对于方法参数列表,这意味着泛型类型绑定到由最左侧参数列表填充的类型:

scala> def meth[A](a1: A, a2: A) = (a1, a2)
meth: [A](a1: A, a2: A)(A, A)

scala> meth(1, "")
res7: (Any, Any) = (1,"")

scala> def meth[A](a1: A)(a2: A) = (a1, a2)
meth: [A](a1: A)(a2: A)(A, A)

scala> meth(1)("")
<console>:10: error: type mismatch;
 found   : String("")
 required: Int
              meth(1)("")
                      ^

可以看出,在第一种情况下Any是推断出来的,而在第二种情况下,会抛出编译器错误,因为类型A受第一个参数列表的约束,而第二个不能再更改它。

但是为了使问题中的方法调用正常工作,Option可能直到到达第二个参数列表时才定义结果的类型。因为这需要从右到左的类型推断,因此会出现错误。这有点等同于List.fold

scala> List(1).foldLeft(Nil)((xs,x) => x::xs)
<console>:8: error: type mismatch;
 found   : List[Int]
 required: scala.collection.immutable.Nil.type
              List(1).foldLeft(Nil)((xs,x) => x::xs)
                                               ^

要使代码正常工作,必须明确指定结果集合的类型,请参阅@rks 答案以获取示例。

请参阅此处的讨论以获取完整解释为什么它被定义为定义。简而言之:Option在很多方面都遵循集合的设计——因此当它的行为方式与集合相同时会更清楚。

于 2012-09-07T15:36:03.773 回答
2

我觉得 sschaef 版本不够精确。我不太了解 scala(事实上,我从未使用过它)但我认为这并不取决于函数的实现方式。我也不明白“打字机从左到右”的事情。

我没有最新版本的 Scala,所以我无法在您的示例 venechka 上进行测试,但我认为可以通过添加一些类型注释来规避输入错误/限制。例如,这里是 sschaef 的示例:

scala> List(1).foldLeft(Nil)((xs,x) => x::xs)
<console>:8: error: type mismatch;
 found   : List[Int]
 required: scala.collection.immutable.Nil.type
              List(1).foldLeft(Nil)((xs,x) => x::xs)
                                               ^

scala> List(1).foldLeft(Nil : List[Int])((xs,x) => x::xs)
res1: List[Int] = List(1)

而且我相信您可以通过执行以下操作在您的示例中做同样的事情:

Some(0).fold(new B : A){_=>new C}

同样,我认为这是 Scala 类型器的限制(可能是由于子类型的存在),但在强烈确认之前我必须环顾四周。

无论如何,在这里和那里添加类型注释应该对你有用,所以享受吧!

编辑:哦,sschaef 编辑了他的答案以做出一些解释,这可能会使我所说的关于这种行为的原因无效。但这并没有改变类型注释将解决您的问题的事实。所以我会让这个消息到位。

于 2012-09-07T16:00:40.590 回答
2

这是类型推断算法的一般限制。跨列表折叠也有同样的限制。引用Scala 中的编程

请注意,两个版本的 flatten 都需要在作为折叠起始值的空列表上添加类型注释。这是由于 Scala 类型推断器的限制,它无法自动推断列表的正确类型。

Scala 的类型推断算法在具有多个参数列表的方法中以增量方式工作。第一个中指定的类型可用于第二个中的推理,第三个中的第二个等。如样式指南中所述,这允许您在fold函数中使用更简单的语法,因为推理引擎知道列表的类型和第一个参数列表中的累加器类型。

但是,由于它在连续参数列表中以增量方式工作,因此推理引擎在将函数参数的类型推断为fold. 相反,您会收到类型错误。

在上面的示例中,只需在累加器值上添加类型注释即可:

Some(0).fold(new B: A){_=>new C}
于 2012-09-07T16:02:20.087 回答