How does the type inferencer work on reduceLeft?

我们两清 提交于 2019-12-22 10:14:31

问题


Further to my other question about reduceLeft, the signature of reduceLeft on Seq is

def reduceLeft [B >: A] (f: (B, A) ⇒ B): B 

and we can call it with expressions such as

List(1,2,3,4) reduceLeft (_ + _)

In this example A is Int, so reduceLeft expects a Function2[B >: Int, Int, B]. Regardless of how reduceLeft works (which is irrelevant), how does the type inferencer know that B has a + method, when it could be of type Any?


回答1:


I think the section 6.26.4 Local Type Inference of the spec sort of explains what's going on. The compiler will look for an optimal type. When the type parameter is contravariant the type chosen will be maximal (in this case Any) and otherwise (invariant or covariant) minimal (in this case Int).

There are a couple examples which I can't really relate to reduceLeft.

What I did notice is the inference seems to happen before looking at the anonymous function passed:

scala> List(1,2).reduceLeft[Any](_.toString + _)
res26: Any = 12

But If I don't help the type inferencer:

scala> List(1,2).reduceLeft(_.toString + _)
<console>:8: error: type mismatch;
 found   : java.lang.String
 required: Int
              List(1,2).reduceLeft(_.toString + _)

Edit, I'm wrong the anonymous function is taken into account, this works:

List(1,2).reduceLeft((_:Any).toString + (_:Any).toString)

There is a compiler -Ytyper-debug option that you can run on:

List(1,2).reduceLeft(_+_)

It will show you that somehow the compiler assumes the expected type of the anonymous function is (Int, Int) => Int, then it proceeds to check the _ + _ against it and succeeds and then infers B as Int. Snippet here:

typed immutable.this.List.apply[Int](1, 2).reduceLeft: [B >: Int](f: (B, Int) => B)B
adapted immutable.this.List.apply[Int](1, 2).reduceLeft: [B >: Int](f: (B, Int) => B)B to ?, undetparams=type B
typing ((x$1, x$2) => x$1.$plus(x$2)): pt = (Int, Int) => Int: undetparams=, 
// some time later 
typed ((x$1: Int, x$2: Int) => x$1.+(x$2)): (Int, Int) => Int
adapted ((x$1: Int, x$2: Int) => x$1.+(x$2)): (Int, Int) => Int to (Int, Int) => Int, 
typed immutable.this.List.apply[Int](1, 2).reduceLeft[Int](((x$1: Int, x$2: Int) => x$1.+(x$2))): Int

I don't know why in absence of type ascription the anonymous function is assumed to be (Int, Int) => Int.




回答2:


If B >: X and the compiler knows X but cannot resolve B it simply assumes B = X.

It is somewhat practical since it only has two options for B and only one is known. So absent knowing which super class it assumes that B is X. You can test the compilers decision making process with the following code.

class Y {
  def bar(y:Y) = this
}
case class X( i: Int ) extends Y {
  def foo(x:X)=X(i+x.i)
}
val t = new Y bar X(7)
val t2 = X(8) bar X(7)
val res = List(X(1),X(2),X(3)) reduceLeft { _ foo _ }
val res2 = List(X(1),X(2),X(3)) reduceLeft { _ bar _ } // will not compile


来源:https://stackoverflow.com/questions/8364931/how-does-the-type-inferencer-work-on-reduceleft

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!