toddaaro
toddaaro

Reputation: 33

Why does Scala's type inferencer fail with this set of implicit arguments involving parameterized types?

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

Answers (2)

Daniel C. Sobral
Daniel C. Sobral

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

Dave L.
Dave L.

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:

  1. I removed the type annotation from bar so you are really just indicating the type once (just in a different spot than you wanted)
  2. I am using App instead of deprecated Application
  3. Using a context bound in the type signature of foo

Upvotes: 1

Related Questions