4

Since Java 8 I can pass intead of

xyz.foreach(e -> System.out.println(e));

I can do the following

xyz.foreach(System.out::println)

I have seen this thread about how method references work, but the problem is the following:

Error:(16, 63) ambiguous reference to overloaded definition,

both method toJson in object IanesJsonHelper of type (source: IanesServer)String

and method toJson in object IanesJsonHelper of type (success: Boolean)String

match expected type Function[?,String]

 val json = IanesJsonHelper.toJson(array,IanesJsonHelper.toJson _)

                                                        ^

I do have 3 functions with the name "toJSON"

def toJson(id: Int): String

and

 def toJson(success: Boolean): String

and

 def toJson(source: IanesServer): String

The last one is the right one.

The function I was calling in the error message above is:

def toJson[T](source: Array[T], toJson: Function[T, String]): String

This is the relevant code:

 val array = new Array[IanesServer](1)
 array(0) = new IanesServer(1, "http://localhost:4567/", "Test")
 val json = IanesJsonHelper.toJson(array,IanesJsonHelper.toJson)

I do not get what my mistake is:

  1. The array is of type IanesServer
  2. T in the calling method should be IanesServer (Array[IanesServer] -> Array[T]
  3. Because of 2. the T in the function must be the same as the T in the array, therefore has to be Function[IanesServer,String] -> Function[T,String]

Can someone please be aso kind as to point out the mistake? At the moment I strongly disagree, that the function is [?,String]. Any ideas?

Answer: Thanks for the quick answer, here is what I chose:

 IanesJsonHelper.toJson[IanesServer](array,IanesJsonHelper.toJson)

1 Answer 1

5
def toJson[T](source: Array[T], toJson: Function[T, String]): String

You expected the compiler to infer toJson to be of type Function[IanesServer, String] because source is of type Array[IanerServer] - and therefore T equals IanesServer.

Unfortunately, the scala compiler is not that smart. There are two ways you can help the compiler here:

  1. State the types explicitly

    IanesJsonHelper.toJson[IanesServer](array, IanesJsonHelper.toJson _)

  2. Split the parameters into two parameter lists:

    def toJson[T](source: Array[T])(toJson: Function[T, String]): String
    
    IanesJsonHelper.toJson(array)(IanesJsonHelper.toJson)
    

When you have two parameter lists, the arguments passed to the first list will tell the compiler how to bind T, and the compiler will use those bindings for the remaining lists.

Here's another shorter example:

// doesn't compile - the compiler doesn't realize `_` is an Int and therefore doesn't know about the `+` operator
def map[A, B](xs: List[A], f: A => B) = ???
map(List(1,2,3), _ + 1)  

//compiles fine
def map[A, B](xs: List[A])(f: A => B) = ???
map(List(1,2,3))(_ + 1)

This behavior may seem unfortunate, but there's a reason for it.

Scala, unlike Java or C#, uses all arguments in a function's parameter list to calculate their LUB (least upper bound) and use that to infer the function's generic type parameters.

For example:

scala> def f[A](x: A, y: A): A = x
f: [A](x: A, y: A)A

scala> f(Some(1), None)
res0: Option[Int] = Some(1)

Here, scala used both arguments (of types Some[Int] and None) to infer the type of A (Option[Int] - the LUB). And this is why scala needs you to tell it which overload of toJson you're referring to.

C#, on the other hand, wouldn't allow this.

Compilation error (line 17, col 3): The type arguments for method 'Program.F(T, T)' cannot be inferred from the usage. Try specifying the type arguments explicitly.

One last note: LUBing is awesome but also presents its disadvantages.

Sign up to request clarification or add additional context in comments.

4 Comments

That's very bad, that the Scala compiler is not that smart. enough. The thing is, that (2) is not working (IntelliJ complains, that no method has that siigneture). I am using " IanesJsonHelper.toJson[IanesServer](array,IanesJsonHelper.toJson)" now. Thanks a lot! PS: Thanks for editing your post so many times :) Did help to make things clearer (no sarcasm, honestly)
@Reisi007 I know, right? This was surprising for me too, especially coming from a C# background where this would compile. Despite this small quirk, I'd still pick scala's type inference over C#'s any day.
I am coming from Java and there is no problem at all.. ( I guess, not tested). I am just getting used to Scala. Thanks for your quick answer anyway. Very appriciated :D
@Reisi007 Upon further reflection, I realized there's a reason why the compiler behaves this way. I've updated my post, hope it helps :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.