When does Scala need parameter types for anonymous and expanded functions?

|▌冷眼眸甩不掉的悲伤 提交于 2019-12-04 11:42:59

问题


When does the Scala compiler really need the type information of parameters of anonymous functions?

For instance, given this function:

def callOn[T,R](target: T, f: (T => R)) = f(target)

then I cannot use it like this:

callOn(4, _.toString)
  => error: missing parameter type for expanded function ((x$1) => x$1.toString)

and I have to specify

callOn(4, (_: Int).toString)

which is rather ugly. Why doesn't my example work, whereas method like map, filter, foldLeft, etc. on the collection classes don't seem to need this explicit type?


回答1:


The trick to type inference is to consider it as an process of iterative refinement. Each parameter block can be used to infer some of the type parameters, which can then be used in subsequent blocks. So take the following definition:

def chain[T,A,B](x: T)(fn1: T=>A)(fn2: A=>B) = fn2(fn1(x))

called as:

chain(2)(_*10)("xxx"+_)

So how is this inferred? First, we start with the block (2) which is known to have the type Int. Substituting that back into the T parameter we get:

def chain[A,B](x: Int)(fn1: Int=>A)(fn2: A=>B) = fn2(fn1(x))

The next parameter block is (_*10), where we now know the type of the placeholder _ to be Int... and multiplying an Int by an Int gives another Int. This is true of the returned type even if an overflow occurrs; at the extreme end it may throw an exception, but exceptions have the type Nothing which is a subclass of everything else in the type system, so we can still say Nothing is-an Int and the inferred type of Int is still valid.

With A inferred, the method becomes:

def chain[B](x: Int)(fn1: Int=>Int)(fn2: Int=>B) = fn2(fn1(x))

Leaving only B which can be inferred from ("xxx"+_). As String + Int is a String, the method is now:

def chain(x: Int)(fn1: Int=>Int)(fn2: Int=>String) = fn2(fn1(x))

As the return type of the method comes direct from fn2, that can also be explicitly shown for completeness:

def chain(x: Int)(fn1: Int=>Int)(fn2: Int=>String): String = fn2(fn1(x))

There you have it, all types safely resolved, and the method proven to be statically valid.


In your case, you need the type T to be inferred before it's possible to infer R from the type T=>R. To do this you must split out the parameters into two distinct blocks, writing the method in a curried form:

def callOn[T,R](target: T)(f: (T => R)) = f(target)



回答2:


This question has also been answered here:

Passing functions for all applicable types around

You expect, ... for Scala's compiler to take into account both parameters to twice to infer the correct types. Scala doesn't do that, though -- it only uses information from one parameter list to the next, but not from one parameter to the next. That mean the parameters f and a [WS: target and f in this case] are analyzed independently, without having the advantage of knowing what the other is.

Note that it does work with a curried version:

scala> def callOn[T,R](target: T)(f: (T => R)) = f(target)
callOn: [T,R](target: T)(f: (T) => R)R

scala> callOn(4)(_.toString)
res0: java.lang.String = 4

scala> 


来源:https://stackoverflow.com/questions/4899320/when-does-scala-need-parameter-types-for-anonymous-and-expanded-functions

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