Implicit conversion from Scala function to Java Fu

2020-07-22 19:13发布

问题:

I would like to create an implicit conversion from a Scala function (possibly anonymous) to java.util.function.Function. Here is what I have:

import java.util.function.{Function => JavaFunction}
implicit def scalaFunctionToJavaFunction[From, To](function: (From) => To): JavaFunction[From, To] = {
  new java.util.function.Function[From, To] {
    override def apply(input: From): To = function(input)
  }
}

It works fine except that the type inference fails when the function being converted doesn't specify parameter type explicitly:

val converted: JavaFunction[String, Int] = (s: String) => s.toInt // works fine
val converted2: JavaFunction[String, Int] = scalaFunctionToJavaFunction(s => s.toInt) // works
val converted3: JavaFunction[String, Int] = s => s.toInt // gives compilation error "missing parameter type"

The compiler is able to infer the type

My questions are:

  • Why cannot Scala compiler infer type of the parameter in the third case?
  • Can I modify the implicit conversion so that the type gets inferred?

I'm aware of a related question which touches on this subject but doesn't give an answer to my questions.

The problem doesn't seem to be related to interoperability with Java, btw. If I replace JavaFunction with a custom Scala trait, the behavior remains the same.

回答1:

Why cannot Scala compiler infer type of the parameter in the third case?

To infer type(s) of parameter(s) of a lambda-expression, the expected type must be String => ? (for this case). In converted3, the expected type is JavaFunction[String, Int]. This isn't a Scala function, so it won't work. It will in Scala 2.12, or in 2.11 with -Xexperimental scalac option. This is according to the specification:

If the expected type of the anonymous function is of the shape scala.Functionn[S1,…,Sn, R], or can be SAM-converted to such a function type, the type Ti of a parameter xi can be omitted, as far as Si is defined in the expected type, and Ti = Si is assumed. Furthermore, the expected type when type checking e is R.

If there is no expected type for the function literal, all formal parameter types Ti must be specified explicitly, and the expected type of e is undefined.

The "SAM-converted" part is the one activated by -Xexperimental/2.12 and not valid in 2.11 by default.

Can I modify the implicit conversion so that the type gets inferred?

I don't believe so. What you can do is to change the place conversion happens: write val unconverted: String => Int = s => s.toInt (or just val unconverted = (s: String) => s.toInt) and pass it where JavaFunction[String, Int] is expected.



回答2:

When you write val foo: SomeType = bar, the compiler is looking for a way to "prove", that the assignment is valid. This can be proven in one of two ways: either bar has a type, that is a subclass of SomeType (or SomeType itself obviously) or there is an implicit conversion from whatever type bar is to SomeType. As yoy can see, in both cases, for the assihnment to work the type of bar has to be known. But when you write val converted3: JavaFunction[String, Int] = s => s.toInt, the type of right hand side is not defined. The compiler knows, that it is some function, returning an Int, but without the type of argument, it is not enough.

Anoher way to say it is that the compiler will not check every implicit conversion in scope, returning something compatible with the LHS, in order to use the conversion, it needs to know the input type.

There is no way around it AFAIK, you have to define the type of the rhs one way or another on order to be able to use the implicit conversion.