Reputation: 33
I would like to define a method parameterized with type T
that has behavior dependent on what implicit argument can be found of type Box[T]
. The following code has this method defined as foo
. When called with foo[Int]
or foo[String]
it will without issue return 1
or "two"
as expected.
Where things get weird is with the method bar. It is defined as returning an Int
, but instead of foo[Int]
I have just foo
. My hope was that the compiler would infer that T
must be of type Int
. It does not do that and instead fails:
bash $ scalac Code.scala
Types.scala:15: error: ambiguous implicit values:
both value one in object Main of type => Main.Box[Int]
and value two in object Main of type => Main.Box[java.lang.String]
match expected type Main.Box[T]
def bar: Int = foo
^
one error found
What is causing this error? Replacing foo
with foo[Int]
compiles fine. The simpler situation where there is no Box[T]
type also compiles fine. That example is also below and uses argle
and bargle
instead of foo
and bar
.
object Main extends Application {
case class Box[T](value: T)
implicit val one = Box(1)
implicit val two = Box("two")
def foo[T](implicit x: Box[T]): T = {
x.value
}
// does not compile:
// def bar: Int = foo
// does compile
def bar: Int = foo[Int]
println(bar)
// prints 1
// the simpler situation where there is no Box type
implicit val three = 3
implicit val four = "four"
def argle[T](implicit x: T): T = x
def bargle: String = argle
println(bargle)
// prints "four"
}
What is going on in this snippet that causes this behavior? What about this interaction of implicit arguments, type inference, and erasure is causing problems? Is there a way to modify this code such that the line def foo: Int = bar
works?
Upvotes: 2
Views: 170
Reputation: 297265
This might be related to SI-3346, though there it is implicit arguments to implicit conversions, and here you have a single implicit.
Upvotes: 1
Reputation: 9801
Someone else will have to explain why the type inference mechanism cannot handle that case, but if you are looking to cleanup your code you could probably do this:
object Test extends App {
case class Box[T](value: T)
implicit val one: Box[Int] = Box(1)
implicit val two: Box[String] = Box("two")
def foo[T : Box]: T = implicitly[Box[T]].value
val bar = foo[Int]
}
Note that:
bar
so you are really just indicating the type once (just in a different spot than you wanted)App
instead of deprecated Application
foo
Upvotes: 1