2017-05-15 2 views
11

J'essaie de comprendre pourquoi exactement une conversion implicite fonctionne dans un cas, mais pas dans l'autre. Voici un exemple:Conversions implicites bizarrerie

case class Wrapper[T](wrapped: T) 
    trait Wrapping { implicit def wrapIt[T](x: Option[T]) = x.map(Wrapper(_)) 

    class NotWorking extends Wrapping { def foo: Option[Wrapper[String]] = Some("foo") } 

    class Working extends Wrapping { 
     def foo: Option[Wrapper[String]] = { 
     val why = Some("foo") 
     why 
     } 
    } 

Fondamentalement, j'ai une conversion implicite Option[T]-Option[Wrapper[T]], et essaie de définir une fonction, qui renvoie une chaîne optionnelle, qui est enveloppé implicitement.

La question est pourquoi, lorsque je tente de retourner Option[String] directement (NotWorking ci-dessus), je reçois une erreur (found : String("foo") required: Wrapper[String]), qui disparaît si je cède le résultat à un val avant de le retourner.

Ce qui donne?

Répondre

10

Je ne sais pas si cela est prévu ou serait considéré comme un bug, mais voici ce que je pense qui se passe.

Dans def foo: Option[Wrapper[String]] = Some("foo") le compilateur définira le type attendu de l'argument fourni à Some() comme Wrapper[String]. Puis il voit que vous avez fourni un String qui n'est pas ce qui est attendu, donc il cherche une conversion implicite String => Wrapper[String], ne peut en trouver un, et échoue.

Pourquoi at-il besoin que prévu des choses de type, et ne pas il suffit de taper Some("foo") comme Some[String] et après essayer de trouver une conversion? Parce que scalac veut être en mesure de typer le code suivant:

case class Invariant[T](t: T) 
val a: Invariant[Any] = Invariant("s") 

Pour que ce code fonctionne, le compilateur ne peut pas il suffit de taper Invariant("s") comme Invariant[String] car alors compilation échouera comme Invariant[String] est pas un sous-type de Invariant[Any]. Le compilateur doit définir le type attendu de "s" à Any afin qu'il puisse voir que "s" est une instance de Any avant qu'il ne soit trop tard. Pour que ce code et votre code fonctionnent correctement, je pense que le compilateur aurait besoin d'une sorte de backtracking logique qu'il ne semble pas avoir, peut-être pour de bonnes raisons.

La raison pour laquelle votre code Working fonctionne, c'est que ce type d'inférence de type ne couvre pas plusieurs lignes. De manière analogue, val a: Invariant[Any] = {val why = Invariant("s"); why}ne compile pas.