3

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.

Community
  • 1
  • 1
Mifeet
  • 12,949
  • 5
  • 60
  • 108
  • A [related article](http://www.tikalk.com/incubator/simulating-sam-closures-scala/) I found. It doesn't provide the answer, but touches on the related topic of Scala function implicit conversion. – Mifeet Apr 24 '16 at 22:40

2 Answers2

3

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.

Community
  • 1
  • 1
Alexey Romanov
  • 167,066
  • 35
  • 309
  • 487
  • Thanks for your reply. Could you please provide some reference for why the expected type has to be `String => ?` ? – Mifeet Apr 24 '16 at 20:29
  • Thank you, that's what I was looking for. – Mifeet Apr 24 '16 at 22:35
  • Another important piece of information is in [section on implicit conversions](http://www.scala-lang.org/files/archive/spec/2.11/06-expressions.html#implicit-conversions). If I understand it correctly, the rules for implicit conversions require the type of the converted value to be known. – Mifeet Apr 24 '16 at 23:04
  • Yes, but it doesn't apply in this case because omitting parameter types is already disallowed by the section I quote, before any implicit conversions would be considered. – Alexey Romanov Apr 25 '16 at 06:36
1

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.

Dima
  • 39,570
  • 6
  • 44
  • 70
  • Thank you for your answer. I don't entirely agree with the statement that the type of `bar` has to be known. E.g. in `val f: Function1[String, Int] = s => s.toInt`, the type of RHS is inferred without explicitly stating that `s` is a `String`. – Mifeet Apr 24 '16 at 22:12
  • @Mifeet in this case the type of rhs is known to be `Function1[String, Int]`, since there is no implicit conversion involved. So, what I said holds: the type of rhs must be known. – Dima Apr 24 '16 at 22:24
  • Well, if you mean "expected type of the anonymous function" is known then I agree. But the type of RHS does not need to be `Function1[String, Int]`, it can be, e.g., `Function1[Object, Int]`. I know those are subtle differences, sorry for nagging :). – Mifeet Apr 24 '16 at 22:34
  • `Function1[Object, Int]` _is_ also a `Function1[String, Int]`, because Function1 is contravariant in the first parameter. – Dima Apr 24 '16 at 22:41
  • It is a subtype. But I know that the word "is" is sometimes used in this meaning, it's the so called "is-a relation" ;). – Mifeet Apr 24 '16 at 22:55