在下面的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定义方式的结果?
谢谢。
答案 0 :(得分: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 answer。
请参阅讨论here以获取完全解释为何定义它的定义。简而言之:Option
在许多方面都遵循集合的设计 - 因此,当它的行为与集合的行为方式相同时,它会更加清晰。
答案 1 :(得分:2)
我觉得sschaef版本不够精确。我不太了解scala(事实上,我从未使用它),但我认为这不取决于函数的实现方式。 我也不明白“typer从左到右”的事情。
我没有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 typer的限制(可能是因为存在子类型),但在强烈肯定之前我必须环顾四周。
无论如何,在这里添加类型注释应该为你做伎俩,所以尽情享受!
编辑:哦,sschaef编辑了他的答案,得到一些解释,可能会使我对这种行为的原因无效。但它并没有改变类型注释将解决您的问题的事实。所以我会让这条消息到位。答案 2 :(得分:2)
这是类型推断算法的一般限制。跨列表的折叠具有相同的限制。引用 Programming in Scala :
请注意,flatten的两个版本都需要一个类型注释 空列表,它是折叠的起始值。这是由于 Scala的类型推理器中的限制,无法推断出 自动更正列表的类型。
Scala的类型推断算法在具有多个参数列表的方法之间递增地工作。第一个中指定的类型可用于第二个中的推理,第三个中的第二个等等As outlined in the style guide,这允许您在fold
函数中使用更简单的语法,因为推理引擎知道类型列表和第一个参数列表中的累加器类型。
但是,由于它在连续参数列表中递增地工作,因此在将函数参数的类型推断为fold
之后,推理引擎将不会返回并更新返回类型(与累加器类型相同) 。相反,您会收到类型错误。
在上面的例子中,只需在累加器值上给出一个类型注释,你就会很好:
Some(0).fold(new B: A){_=>new C}