2013-06-24 2 views
5

Ce qui suit est une version simplifiée de mon vrai problème:Scala: bug dans le paramètre implicite

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 

Cela fonctionne, avec b converti implicitement o (e) (f (E.z)). Mais avec les petites modifications suivantes:

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

il ne parvient à trouver la E.z valeur implicite appropriée bien qu'il n'y ait pas de différence essentielle du code d'origine, tandis que la conversion manuelle explicite à o (e) (f (E.z)) fonctionne toujours.

Je sais que l'implémentation du paramètre implicite n'est pas encore terminée et qu'il y a encore beaucoup de problèmes non résolus. Si c'est l'un d'entre eux, j'aimerais le signaler aux contributeurs Scala. Donc ma question est, a) est-ce vraiment un bug? b) si c'est le cas, où et comment puis-je déposer un bug afin qu'il puisse être corrigé dans le futur?

MISE À JOUR

Travis réponse a fonctionné comme un charme! Soit dit en passant, le code ci-dessus était une solution à mon problème d'origine:

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) 

Dans ce code, la situation était l'inverse: il fonctionne avec la version setter mais pas avec la version getter plus simple. Le compilateur se plaint qu'il est difficile d'utiliser E ou F comme paramètre implicite, bien que l'utilisation de F ne compile pas et n'a pas de sens. J'ai réussi à le faire fonctionner en faisant une chose semblable:

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

Cela fonctionne, et bien que je pouvais le faire fonctionner en quelque sorte, je ne comprends toujours pas la logique derrière cette magie.

+0

Intéressant. En cours d'exécution avec '-Xlog-implicits', il semble que' T' dans 'f [T: Z]' ne soit pas inféré comme 'E.Value'. Donc ceci fonctionne: 'val b: Int = o (e) (f [E.Value])' mais cela ne fonctionne pas: 'val b: Int = o (e) (f)'. – gourlaysama

Répondre

6

Vous avez déjà rencontré une autre variante de this limitation du système d'inférence de type Scala.

Le compilateur résoudra le T pour f si dans le premier cas, où il cherche une conversion implicite de la vieille E.Value plaine à Int, mais pas dans la seconde, où il veut une conversion de E.Value => Unit (c.-à-Function1[E.Value, Unit]) à Int.

Heureusement, il y a une solution facile dans des cas comme celui-ci, il suffit d'utiliser une vue liée:

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

Cela desugar à quelque chose comme ce qui suit:

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

Maintenant, quand le compilateur veut une conversion de E.Value => Unit à Int il sera en mesure de résoudre F à E.Value => Unit immédiatement et puis T à E.Value.

+0

Merci pour la réponse, cela a fonctionné comme un charme! Mais je ne comprends toujours pas comment fonctionne cette magie. Pouvez-vous jeter un oeil à la mise à jour ci-dessus et donner une explication? On dirait que S n'a même pas besoin de faire fonctionner le système d'inférence de type. Mystérieux. –

Questions connexes