Why is a reference to an overloaded definition ambiguous when types are known?

I have a function like this:

def ifSome[B, _](pairs:(Option[B], B => _)*) { for((paramOption, setFunc) <- pairs) for(someParam <- paramOption) setFunc(someParam) } 

and overloaded functions like these:

 class Foo{ var b="" def setB(b:String){this.b = b} def setB(b:Int){this.b = b.toString} } val f = new Foo 

then the following line throws an error:

 ifSome(Option("hi") -> f.setB _) <console>:11: error: ambiguous reference to overloaded definition, both method setB in class Foo of type (b: Int)Unit and method setB in class Foo of type (b: String)Unit match expected type ? ifSome(Option("hi") -> f.setB _) 

But the compiler knows that we are looking for Function1 [java.lang.String, _], so why is the presence of Function1 [Int, _] confusing? Am I missing something or is it a compiler error (or maybe it should be a function request)?

I managed to get around this using type annotation like

 ifSome(Option("hi") -> (f.setB _:String=>Unit)) 

but I would like to understand why this is necessary.

+4
source share
2 answers

You need to try $ scalac -Ydebug -Yinfer-debug x.scala , but first you want to minimize it.

In this case, you will see how, in the version with curry, B is solved in the first parameter list:

 [infer method] solving for B in (bs: B*)(bfs: Function1[B, _]*)Nothing based on (String)(bfs: Function1[B, _]*)Nothing (solved: B=String) 

For the unladen version, you will see weirdness around

 [infer view] <empty> with pt=String => Int 

as it tries to eliminate the overload, which may lead you to a weird solution below.

The fictitious implicit serves the sole purpose of allowing overload so that a conclusion can be made. The implicit unused itself and remains unrealized ???

This is a rather strange decision, but you know that overloading is evil, right? And you must fight evil with any tools at your disposal.

Also see that a workaround for annotating a type is more time consuming than a regular parameter type in normal mode.

 object Test extends App { def f[B](pairs: (B, B => _)*) = ??? def f2[B](bs: B*)(bfs: (B => _)*) = ??? def g(b: String) = ??? def g(b: Int) = ??? // explicitly f[String](Pair("hi", g _)) // solves for B in first ps f2("hi")(g _) // using Pair instead of arrow means less debug output //f(Pair("hi", g _)) locally { // unused, but selects g(String) and solves B=String import language.implicitConversions implicit def cnv1(v: String): Int = ??? f(Pair("hi", g _)) } // a more heavy-handed way to fix the type class P[A](a: A, fnc: A => _) class PS(a: String, fnc: String => _) extends P[String](a, fnc) def p[A](ps: P[A]*) = ??? p(new PS("hi", g _)) } 
+2
source

Type input in Scala only works from one parameter list to the next. Since your ifSome has only one parameter list, Scala does not output anything. You can change ifSome as follows:

 def ifSome[B, _](opts:Option[B]*)(funs: (B => _)*) { val pairs = opts.zip(funs) for((paramOption, setFunc) <- pairs) for(someParam <- paramOption) setFunc(someParam) } 

leave foo as is ...

 class Foo{ var b="" def setB(b:String){this.b = b} def setB(b:Int){this.b = b.toString} } val f = new Foo 

And change the call to ifSome accordingly:

 ifSome(Option("hi"))(f.setB _) 

And it all works. Now, of course, you should check if the opts and funs tags funs the same length at runtime.

+2
source

Source: https://habr.com/ru/post/1436903/


All Articles