Florian Reisinger
Florian Reisinger

Reputation: 3088

Passing function in Scala

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)

Upvotes: 4

Views: 424

Answers (1)

dcastro
dcastro

Reputation: 68710

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.

Upvotes: 5

Related Questions