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.
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.
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.