Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Could not find implicit value while using Context Bound

I'm using the following code written in Scala 2.11.8:

  sealed trait Acceptable[T]

  object Acceptable {
    implicit object Int extends Acceptable[Int]

    implicit object String extends Acceptable[String]
  }

  case class Enc[T](f: T => Any)

  implicit def test[I, T](implicit f: I => T, a: Acceptable[T]): Enc[I] =
    Enc[I](f)

  val e = implicitly[Enc[Int]]

It successfully compiles.

As you can see a: Acceptable[T] parameter should be easily converted to context bound:

implicit def test[I, T: Acceptable](implicit f: I => T): Enc[I] =
    Enc[I](f)

But after that change compilation is starting to fail with error:

could not find implicit value for parameter e: app.Enc[Int]

Why does this happen?

UPDATE:

I tried -Xlog-implicits compiler option and compilation log gives me:

[info] /path/to/ScalaTest/src/main/scala/app/Main.scala:60: test is not a valid implicit value for app.Enc[Int] because:
[info] hasMatchingSymbol reported error: ambiguous implicit values:
[info]  both object Int in object Acceptable of type app.Acceptable.Int.type
[info]  and object String in object Acceptable of type app.Acceptable.String.type
[info]  match expected type app.Acceptable[T]
[info]   val e = implicitly[Enc[Int]]
[info]                     ^
[info] /path/to/ScalaTest/src/main/scala/app/Main.scala:60: app.test is not a valid implicit value for app.Enc[Int] because:
[info] hasMatchingSymbol reported error: ambiguous implicit values:
[info]  both object Int in object Acceptable of type app.Acceptable.Int.type
[info]  and object String in object Acceptable of type app.Acceptable.String.type
[info]  match expected type app.Acceptable[T]
[info]   val e = implicitly[Enc[Int]]
[info]                     ^
[error] /path/to/ScalaTest/src/main/scala/app/Main.scala:60: could not find implicit value for parameter e: app.Enc[Int]
[error]   val e = implicitly[Enc[Int]]

Ok, I understand this output. But why does it work in case of implicit parameter?

like image 547
mixel Avatar asked Nov 27 '16 21:11

mixel


1 Answers

I don't have a reference for this, but in my experience implicits corresponding to a context bound are searched for before any other "explicit" implicit parameters; your method

implicit def test[I, T: Acceptable](implicit f: I => T): Enc[I] =
  Enc[I](f)

is equivalent to

implicit def test2[I, T](implicit a: Acceptable[T], f: I => T): Enc[I] =
  Enc[I](f)

which as you can easily check doesn't work either. Why? from the output it looks like the compiler first tries to look for an implicit Acceptable[T] and it fails at this point, due to ambiguity; at this point it stops searching for anything else. What is confusing is the error message, which IMHO should be something like "failed searching for Acceptable[T]: ambiguous implicits" or something like it.

Why the other method works? because of the order of the implicit parameters. The compiler will search first for f: I => T and this will most likely bind T to Int, and then we do have a unique Acceptable[Int] implicit in scope. In general

  • I wouldn't mix context bounds and implicit parameters
  • The order of implicit parameters is important, and they should be arranged so that finding one determines the next uniquely

As far as I know all this is not spec'ed and depends on the current implementation; the above is based mostly in my experience debugging implicits errors.

like image 68
Eduardo Pareja Tobes Avatar answered Nov 15 '22 05:11

Eduardo Pareja Tobes