Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala: bug in implicit parameter

The following is a simplified version of my real problem:

class Z[T]
object E extends Enumeration {
  implicit val z = new Z[Value] 
  val X, Y = Value
}
implicit def f[T : Z] = (getter: T) => 0
implicit def o[T](v: Option[T])(implicit toInt: T => Int) = 0
def e: Option[E.Value] = null
val b: Int = e

This works, with b implicitly converted to o(e)(f(E.z)). But with the small changes following:

implicit def f[T : Z] = (setter: T => Unit) => 0
implicit def o[T](v: Option[T])(implicit toInt: (T => Unit) => Int) = 0

it fails finding the appropriate implicit value E.z although there's no essential difference from the original code, while manual explicit conversion to o(e)(f(E.z)) still works.

I know the implementation of implicit parameter is not complete yet and there are still many unresolved issues. If this is one of them, I'd like to report it to the Scala contributors. So my question is, a) is this really a bug? b) if so, where and how can I file a bug so that it could be fixed in the future?

UPDATE

Travis' answer worked like a charm! By the way, the code above was a workaround to my original problem:

implicit object E extends Enumeration { val X, Y = Value }
implicit object F extends Enumeration { val X, Y = Value }
implicit def f[T <: Enumeration](getter: T#Value)(implicit e: T) = 0
implicit def o[T](v: Option[T])(implicit toInt: T => Int) = 0
val b: Int = Some[E.Value](null)

In this code, the situation was the other way around: it works with the setter version but not with the simpler getter version. The compiler complains that it's confusing whether to use E or F as the implicit parameter though using F doesn't actually compile nor make sense. I managed to get it working by doing a similar thing:

implicit def f[S <% T => T, T <: Enumeration](getter: T#Value)(implicit e: T) = 0

This works, and although I somehow could get it working, I still don't understand the logic behind this magic.

like image 294
K J Avatar asked Jun 24 '13 19:06

K J


1 Answers

You've run into a yet another variation of this limitation of Scala's type inference system.

The compiler will resolve the T for f if in the first case, where it's looking for an implicit conversion from plain old E.Value to Int, but not in the second, where it wants a conversion from E.Value => Unit (i.e., Function1[E.Value, Unit]) to Int.

Fortunately there's an easy workaround in cases like this—just use a view bound:

implicit def f[F <% T => Unit, T: Z] = (setter: F) => 0

This will desugar to something like the following:

implicit def f[F, T](implicit st: F <:< (T => Unit), ev: Z[T]) = (setter: F) => 0

Now when the compiler wants a conversion from E.Value => Unit to Int it'll be able to resolve F to E.Value => Unit immediately and then T to E.Value.

like image 84
Travis Brown Avatar answered Nov 15 '22 05:11

Travis Brown