2012-05-26 14 views
6

Sto usando scala 2.10.0-snapshot datato (20.120.522) e hanno i seguenti file Scala:Risoluzione implicita in scala 2.10.x. cosa sta succedendo?

questo definisce il typeclass e un'istanza typeclass base:

package com.netgents.typeclass.hole 

case class Rabbit 

trait Hole[A] { 
    def findHole(x: A): String 
} 

object Hole { 
    def apply[A: Hole] = implicitly[Hole[A]] 
    implicit val rabbitHoleInHole = new Hole[Rabbit] { 
    def findHole(x: Rabbit) = "Rabbit found the hole in Hole companion object" 
    } 
} 

questo è l'oggetto del pacchetto:

package com.netgents.typeclass 

package object hole { 

    def findHole[A: Hole](x: A) = Hole[A].findHole(x) 

    implicit val rabbitHoleInHolePackage = new Hole[Rabbit] { 
    def findHole(x: Rabbit) = "Rabbit found the hole in Hole package object" 
    } 
} 

e qui è la prova:

package com.netgents.typeclass.hole 

object Test extends App { 

    implicit val rabbitHoleInOuterTest = new Hole[Rabbit] { 
    def findHole(x: Rabbit) = "Rabbit found the hole in outer Test object" 
    } 

    { 
    implicit val rabbitHoleInInnerTest = new Hole[Rabbit] { 
     def findHole(x: Rabbit) = "Rabbit found the hole in inner Test object" 
    } 

    println(findHole(Rabbit())) 
    } 
} 

Come potete vedere, Hole è un semplice typeclass che definisce un metodo che un Rabbit sta cercando di trovare. Sto cercando di capire le implicite regole di risoluzione su di esso.

  • con tutti e quattro i casi typeclass non commentate, scalac lamenta ambiguità su rabbitHoleInHolePackage e rabbitHoleInHole. (Perché?)

  • se commento fuori rabbitHoleInHole, scalac compila e torno "Coniglio ha trovato il foro nell'oggetto pacchetto Foro". (Non dovrebbe impliciti in ambito locale hanno la precedenza?)

  • Se dunque io commento fuori rabbitHoleInHolePackage, scalac lamenta ambiguità su rabbitHoleInOuterTest e rabbitHoleInInnerTest. (Perché? Nell'articolo di eed3si9n, URL di seguito, ha trovato impliciti btw interni ed esterni portata possono avere la precedenza diverso.)

  • Se dunque io commento fuori rabbitHoleInInnerTest, scalac compila e torno "Rabbit trovato il buco nell'oggetto Test esterno ".

Come si può vedere, i comportamenti di cui sopra non seguono affatto le regole che ho letto sulla risoluzione implicita. Ho descritto solo una minima parte delle combinazioni che puoi fare per commentare/escludere le istanze e la maggior parte di esse sono davvero molto strane - e non ho ancora ottenuto importazioni e sottoclassi.

Ho letto e guardato presentation by suereth, stackoverflow answer by sobral e a very elaborate revisit by eed3si9n, ma sono ancora completamente sconcertato.

+1

Hai provato con 2.9.x? –

+0

i comportamenti sono diversi rispetto a 2.9.2 * con tutti e quattro i casi typeclass commentata, scalac compila e torno "Rabbit trovato il buco nel oggetto di prova interno" * se ho commentato rabbitHoleInHole, scalac compila e ottengo indietro "Coniglio ha trovato il buco nell'oggetto Test interno". * se poi ho commentato rabbitHoleInHolePackage, scalac compila e torno "Rabbit ha trovato il buco nell'oggetto Test interno". * se poi ho commentato rabbitHoleInInnerTest, scalac compila e torno "Rabbit ha trovato il buco nell'oggetto Test esterno". –

+0

C'era un bug nelle regole di inferenza con Scala fino a 2.9, che è stato scoperto da qualcuno qui su Stack Overflow che ha cercato di capire come le specifiche dettavano il comportamento che stava vedendo. –

risposta

4

Cominciamo con gli impliciti nell'oggetto pacchetto e il tipo di compagno di classe disabile:

package rabbit { 
    trait TC 

    object Test extends App { 
    implicit object testInstance1 extends TC { override def toString = "test1" } 

    { 
     implicit object testInstance2 extends TC { override def toString = "test2" } 

     println(implicitly[TC]) 
    } 
    } 
} 

scalac cerca qualsiasi in ambito impliciti, trova testInstance1 e testInstance2. Il fatto che uno sia in un ambito più stretto è rilevante solo se hanno lo stesso nome - si applicano le normali regole dell'ombra. Abbiamo scelto nomi distinti e in questo caso né implicito è più specifico dell'altro, quindi un'ambiguità viene riportata correttamente.

Proviamo un altro esempio, questa volta eseguiremo un implicito nello scope locale rispetto a uno nell'oggetto pacchetto.

package rabbit { 
    object `package` { 
    implicit object packageInstance extends TC { override def toString = "package" } 
    } 

    trait TC 

    object Test extends App { 
    { 
     implicit object testInstance2 extends TC { override def toString = "test2" } 

     println(implicitly[TC]) 
    } 
    } 
} 

Cosa succede qui? La prima fase della ricerca implicita, come prima, considera tutti gli impliciti nello scope nel sito di chiamata. In questo caso, abbiamo testInstance2 e packageInstance.Questi sono ambigui, ma prima di segnalare tale errore, la seconda fase entra in gioco e cerca lo scope implicito di TC.

Ma quale è lo scopo implicito qui? TC non ha nemmeno un oggetto complementare? Abbiamo bisogno di rivedere la definizione precisa qui, in 7.2 della Scala Reference.

L'ambito implicito di un tipo T è costituito da tutti i moduli compagno (§5.4) di classi che sono associati con il tipo di parametro implicito . Qui, diciamo una classe C è associato a un tipo T, se è una classe base (§5.1.2) di una parte di T.

Le parti di un tipo T sono:

  • se T è un tipo composto T1 with ... with Tn, l'unione delle parti di T1, ..., Tn, nonché T stessa,
  • se T è un tipo parametrizzato S[T1, ..., Tn], l'unione delle parti di S e T1,...,Tn,
  • se T è un tipo singoletto p.type, le parti del tipo di p,
  • se T è un tipo di proiezione S#U, le parti di S nonché T stessa,
  • in tutti gli altri casi, solo T stesso.

Stiamo cercando rabbit.TC. Dal punto di vista del sistema di tipi, questa è una stenografia per: rabbit.type#TC, dove rabbit.type è un tipo che rappresenta il pacchetto, come se fosse un oggetto normale. Richiamando la regola 4, ci fornisce le parti TC e p.type.

Quindi, cosa significa tutto ciò? Anche i membri impliciti nell'oggetto del pacchetto fanno parte dell'ambito implicito!

Nell'esempio sopra, questo ci offre una scelta non ambigua nella seconda fase della ricerca implicita.

Gli altri esempi possono essere spiegati allo stesso modo.

In sintesi:

  • ricerca impliciti procede in due fasi. Le solite regole di importazione e ombreggiamento determinano un elenco di candidati.
  • membri impliciti in un oggetto contenitore incluso possono essere inclusi nell'ambito, supponendo che si stia utilizzando nested packages.
  • Se ci sono più candidati, le regole di sovraccarico statico vengono utilizzate per verificare se esiste un vincitore. Un addiotnal a tiebreaker, il compilatore preferisce uno implicito rispetto ad un altro definito in una superclasse del primo.
  • Se la prima fase fallisce, lo scope implicito viene consultato allo stesso modo. (Una differenza è che i membri impliciti di diversi compagni possono avere lo stesso nome senza ombreggiarsi a vicenda.
  • Anche gli impliciti negli oggetti pacchetto dai pacchetti di inclusione fanno parte di questo ambito implicito.

UPDATE

In Scala 2.9.2, il comportamento è diverso e sbagliato.

package rabbit { 
    trait TC 

    object Test extends App { 
    implicit object testInstance1 extends TC { override def toString = "test1" } 

    { 
     implicit object testInstance2 extends TC { override def toString = "test2" } 

     // wrongly considered non-ambiguous in 2.9.2. The sub-class rule 
     // incorrectly considers: 
     // 
     // isProperSubClassOrObject(value <local Test>, object Test) 
     // isProperSubClassOrObject(value <local Test>, {object Test}.linkedClassOfClass) 
     // isProperSubClassOrObject(value <local Test>, <none>) 
     //  (value <local Test>) isSubClass <none> 
     //  <notype> baseTypeIndex <none> >= 0 
     //  0 >= 0 
     //  true 
     //  true 
     // true 
     // true 
     // 
     // 2.10.x correctly reports the ambiguity, since the fix for 
     // 
     // https://issues.scala-lang.org/browse/SI-5354?focusedCommentId=57914#comment-57914 
     // https://github.com/scala/scala/commit/6975b4888d 
     // 
     println(implicitly[TC]) 
    } 
    } 
} 
+0

BTW, 'scalac -Yinfer-debug' aiuta i candidati e gli impliciti scelti. – retronym

+0

sì, posso duplicare gli stessi comportamenti in 2.10.x e la tua spiegazione della risoluzione implicita è abbastanza chiara. tuttavia, i comportamenti osservati usando 2.9.2 erano abbastanza diversi. nel primo caso in cui si disabilitano entrambi impliciti nell'oggetto pacchetto e nell'oggetto companion typeclass, il codice viene compilato senza alcun avviso di ambiguità e restituito "test2" quando viene eseguito. nel secondo caso con l'implicito nell'oggetto pacchetto abilitato, il codice compilato come dichiarato ma "test2" è stato stampato al posto di "pacchetto". il trattamento della risoluzione implicita è cambiato in 2.10.x? –

+0

Sembra un insetto sottile in ['isProperSubClassOrObject'] (https://github.com/scala/scala/blob/v2.9.2/src/compiler/scala/tools/nsc/typechecker/Infer.scala#L984) - - non tiene conto del fatto che 'sym2' potrebbe essere un blocco locale, che ha' 'come compagno. Non ho capito cosa è cambiato in 2.10.x per evitare questo. – retronym

Problemi correlati