Scala 3 contextual abstractions conflict with overloading - scala

Contextual abstractions seem to interfere with overloading, and produce unexpected compilation error messages. This here works:
def foo(b: Double ?=> Unit): Double ?=> Unit = ()
// def foo(a: String): Unit = () // uncommenting causes compilation error
#main def demo(): Unit =
given c: Double = 299792458.0
foo {
println("ok")
}
But as soon as we uncomment the overloaded def foo, it no longer compiles with the error message
None of the overloaded alternatives of method foo in package p with types
(a: String): Unit
(b: (Double) ?=> Unit): (Double) ?=> Unit
match arguments (Unit)
(It's same on 3.1.0 and 3.0.x)
Any ideas what exactly happens here?
Is there any reason why this would be the expected behavior?
Any proposals on how to make it compile with two overloaded foo definitions?

Related

-Xlint doesn't warn when the inferred type arg is widened too much

Tour of Scala: Lower Type Bounds says compiling the code with -Xlint shoud give me a warning.
Here is my adaptation of the code. But I don't get any warning when I run scalac -Xlint test.scala. Why?
scalac version is 2.13.8
trait List[+A] {
def prepend[B >: A](elem: B): NonEmptyList[B] = NonEmptyList(elem, this)
}
case class NonEmptyList[+A](head: A, tail: List[A]) extends List[A]
object Nil extends List[Nothing]
trait Bird
case class AfricanSwallow() extends Bird
case class EuropeanSwallow() extends Bird
object hello {
def main(args: Array[String]): Unit = {
val africanSwallows: List[AfricanSwallow] = Nil.prepend(AfricanSwallow())
val swallowsFromAntarctica: List[Bird] = Nil
val someBird: Bird = EuropeanSwallow()
val birds: List[Bird] = africanSwallows
val someBirds = africanSwallows.prepend(someBird)
val moreBirds = birds.prepend(EuropeanSwallow())
val allBirds = africanSwallows.prepend(EuropeanSwallow())
val error = moreBirds.prepend(swallowsFromAntarctica) // List[Object]
}
}
I think it's related to the scalac version. I think you should explicitly declare which warning you want lint to warn you. Like imagine this zero-ary eta expansion:
def myFunction(): Unit = ()
def accept(zeroAry: () => Unit): Unit = println("accepted")
accept(myFunction)
You need to explicitly tell lint to warn you when this happens:
> scalac -Xlint:eta-zero src/Main.scala
src/Main.scala:7: warning: An unapplied 0-arity method was eta-expanded (due to the expected type () => Unit), rather than applied to `()`.
Write myFunction() to invoke method myFunction, or change the expected type.
accept(myFunction)
^
1 warning
Also, you may want the compiler to both warn you this, and detached function documentations, and some other ones, then you have to explicitly use them all together:
scalac -Xlint:eta-zero,doc-detached,some_other_warning,another_one src/Main.scala
Alternatively, you can use a wildcard so scalac will just enable lint for all the warnings:
scalac -Xlint:_ src/Main.scala

ScalaMock: Is it possible to mock a trait with an overloaded method expecting a curried function parameter?

My application is using Scala 2.13 and ScalaMock 5.1. Is it possible to mock the bar method in a trait like this one?
trait Foo {
def bar(f: Double): Double
def bar(i: Int)(f: => Double): Double
}
The following attempt does not compile. The error from the compiler is: _ must follow method; cannot follow Double
val mockFoo = mock[Foo]
(mockFoo.bar(_: Int)(_: Double) _).expects(1, 1.0).returns(2.0)
This attempt also does not compile. The error from the compiler is: type mismatch; found: () => Double, required: Double
val mockFoo = mock[Foo]
(mockFoo.bar(_: Int)(_: () => Double) _).expects(1, 1.0).returns(2.0)
This attempt does not compile. It returns the same error as the previous attempt.
val mockFoo = mock[Foo]
(mockFoo.bar(_: Int)(_: () => Double)).expects(1, 1.0).returns(2.0)
And this attempt also fails to compile. This time, the error is Unable to resolve overloaded method bar
val mockFoo = mock[Foo]
(mockFoo.bar(_: Int)(_: Double)).expects(1, 1.0).returns(2.0)

Scala implicit implicit

I'm trying to do some implicit magic in my code but the issue is very simple and I have extracted it out here. It seems a bit strange since from what I've read the following should work.
implicit class Foo(value: Double) {
def twice = 2*value
}
2.0.twice
implicit def strToDouble(x: String) = Try(x.toDouble) match {
case Success(d) => d
case Failure(_) => 0.0
}
strToDouble("2.0").twice
val a: Double = "2.0"
val b: Double = "equals 0.0"
"2.0".twice
I get a compile error
value twice is not a member of String
[error] "2.0".twice
I get you compiler, twice is defined for Doubles, not Strings. But I did tell you how to go from Strings to Doubles, and there is no ambiguity here (as far as I can tell), so shouldn't you be able to note that "2.0".twice can be done by doing strToDouble("2.0").twice?
Am I missing something here? Or is this an optimisation so that the compiler doesn't try out all the possible permutations of implicits (which would grow super-exponentially, I think as a factorial). I suppose I'm looking for a confirmation or rejection of this really.
Thanks
If you want extension method to be applicable even after implicit conversion, you can fix the definition of implicit class
implicit class Foo[A](value: A)(implicit ev: A => Double) {
def twice: Double = 2 * value
}
implicit def strToDouble(x: String): Double = ???
2.0.twice //compiles
"2.0".twice //compiles
I get you compiler, twice is defined for Doubles, not Strings. But I
did tell you how to go from Strings to Doubles, and there is no
ambiguity here (as far as I can tell), so shouldn't you be able to
note that "2.0".twice can be done by doing strToDouble("2.0").twice?
According to specification implicit conversions are applicable in three cases only
Why can't the compiler select the correct String.contains method when using this lambda shorthand?
https://scala-lang.org/files/archive/spec/2.13/07-implicits.html#views
The conversion of 2.0.twice to Foo(2.0).twice is the 2nd case and the conversion of "2.0" to strToDouble("2.0") is the 1st case. As you can see there is no item that they can be applied together. So if you want them to be applicable together you should specify that explicitly like I showed above.
Similarly if you defined conversions from A to B and from B to C this doesn't mean you have a conversion from A to C
case class A(i: Int)
case class B(i: Int)
case class C(i: Int)
implicit def aToB(a: A): B = B(a.i)
implicit def bToC(b: B): C = C(b.i)
A(1): B // compiles
B(1): C // compiles
// A(1): C //doesn't compile

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

I have a function like so:
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 produces 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're looking for a Function1[java.lang.String, _], so why should the presence of a Function1[Int, _] present any confusion? Am I missing something or is this a compiler bug (or perhaps it should be a feature request)?
I was able to workaround this by using a type annotation like so
ifSome(Option("hi") -> (f.setB _:String=>Unit))
but I'd like to understand why this is necessary.
You'll want to try $ scalac -Ydebug -Yinfer-debug x.scala but first you'll want to minimize.
In this case, you'll see how in the curried version, B is solved in the first param 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 uncurried version, you'll see some strangeness around
[infer view] <empty> with pt=String => Int
as it tries to disambiguate the overload, which may lead you to the weird solution below.
The dummy implicit serves the sole purpose of resolving the overload so that inference can get on with it. The implicit itself is unused and remains unimplemented???
That's a pretty weird solution, but you know that overloading is evil, right? And you've got to fight evil with whatever tools are at your disposal.
Also see that your type annotation workaround is more laborious than just specifying the type param in the normal way.
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 _))
}
Type inference in Scala only works from one parameter list to the next. Since your ifSome only has one parameter list, Scala won't infer 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 it 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 have to check whether opts and funs have the same length at runtime.

ambiguous implicit conversion errors

Here is a code snippet that tries to reproduce a problem I am facing while implementing an internal DSL:
object testObj {
implicit def foo1[T <% Function1[Int, Int]](fun: T): String = "foo1"
implicit def foo2[T <% Function2[Int, Int, Int]](fun: T): String = "foo2"
def test(arg: String): Unit = {}
test((x:Int) => 5) //Ambiguous implicit conversion error
test((x:Int, y:Int) => 5) //Ambiguous implicit conversion error
}
I am getting ambiguous implicit conversions errors at the shown locations:
<console>:21: error: type mismatch;
found : Int => Int
required: String
Note that implicit conversions are not applicable because they are ambiguous:
both method foo1 in object testObj of type [T](fun: T)(implicit evidence$1: T => (Int => Int))String
and method foo2 in object testObj of type [T](fun: T)(implicit evidence$2: T => ((Int, Int) => Int))String
are possible conversion functions from Int => Int to String
test((x:Int) => 5) //Ambiguous implicit conversion error
^
However commenting one of the implicits does not solve the problem. I am using view bounds since finally I want to chain the implicits. Note that the code snippet given above does not involve implicit chaining.
I was expecting that foo1 implicit conversion would be applicable for the first test application whereas foo2 implicit conversion would be applicable for the second test application.
I don't understand how both the implicits are applicable to both the testfunction applications. Why is this happening and how to make this work?
Edit:
If I don't use view bounds, it works fine as shown below. But I want to use view bounds since I want to chain the implicits the way it is explained in the post How can I chain implicits in Scala?.
implicit def foo1(fun: Function1[Int, Int]): String = "foo1"
implicit def foo2(fun: Function2[Int, Int, Int]): String = "foo2"
def test(arg: String): Unit = {}
test((x:Int) => 5) //No error
test((x:Int, y:Int) => 5) //No error
I'm afraid this won't work. View bounds are just not taken into account when resolving implicits and thus you can't chain implicits. That is be design, because such chaining could create some very unreadable code. The only option I see is to create a new implicit conversion for each possible chain of conversions.