Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Option.fold in scala 2.10

In the following session with 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}

I would expect the compiler find the common supertype (namely A) rather than complaining. Is it the general type inference limitation, or the consequence of the way Option.fold is defined?

Thank you.

like image 687
venechka Avatar asked Dec 08 '22 21:12

venechka


1 Answers

The problem results of a combination of Scalas type inference algorithm and the way how Option.fold is defined.

Scalas type inference works from left to right, this means that it starts with the leftmost symbol to search for a possible type for an expression. Further, for method parameter lists this means, that a generic type is bound to the type filled in by the leftmost parameter list:

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)("")
                      ^

As one can see, in the first case Any is inferred, whereas in the second case a compiler error is thrown because the type of A is bound by the first parameter list and the second one can't change it any more.

But in order to get he method call in the question to work, the type of the resulting Option may not defined until the second parameter list is reached. Because this requires a right to left type inference hence the error. This is somewhat identical to 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)
                                               ^

To get the code to work one has to explicitly specify the type of the resulting collection, see @rks answer for an example.

See the discussion here for the full explanation why it is defined as it is defined. In short: Option follows the design of collections in a lot of respects - thus it is more clear when it behaves the same way as collections do.

like image 137
kiritsuku Avatar answered Dec 21 '22 18:12

kiritsuku