scala 2.10中的Option.fold

时间:2012-09-07 15:31:25

标签: scala scala-2.10

在下面的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定义方式的结果?

谢谢。

3 个答案:

答案 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}