2016-01-22 33 views
13

Poniższy kod nie kompiluje:Scala niejawna konwersja z dominującą cechą

import scala.language.implicitConversions 

trait Base { 
    class Wrp[+T](val v: T) // wrapper/internal representation 
} 

trait BooleanOps extends Base { 
    // implicit conversion 
    implicit def lift2BooleanOpsCls(x: Boolean): BooleanOpsCls = 
    new BooleanOpsCls(new Wrp[Boolean](x)) 
    class BooleanOpsCls(wx: Wrp[Boolean]) { 
    def ||(wy: =>Wrp[Boolean]): Wrp[Boolean] = new Wrp[Boolean](wx.v || wy.v) 
    } 
} 

trait MyExample extends BooleanOps { 
    // test method 
    def foo(): Wrp[Boolean] = { 
    val ret: Wrp[Boolean] = false || new Wrp[Boolean](true) 
    ret 
    } 
} 

wyjściowy:

MyExample.scala:18: error: type mismatch; 
found : MyExample.this.Wrp[Boolean] 
required: Boolean 
     val ret: Wrp[Boolean] = false || new Wrp[Boolean](true) 
             ^

Ale jeśli:

1) umieścić class Wrp poza Bazy

lub

2) przenieś ciało BooleanOps do MyExample

wszystko kompiluje.

Dlaczego oryginalny przykład nie działa? Jeśli masz wgląd w to zachowanie, pomoc będzie doceniona. Dziękuję Ci.

Odpowiedz

1

Oryginalny przykład będzie działał, jeśli zmienisz nazwę metody ||. Kompilator znajduje metodę false.||() i nie zadaje sobie trudu znalezienia ukrytej funkcji, która również mogłaby tam działać.

+0

Rzeczywiście działa to, ale próbuję przeciążać zachowanie operatora "||". Kompilator znajduje metodę o tej samej nazwie ('||'), ale typ argumentu jest inny ('Wrp [Boolean]' zamiast 'Boolean'), więc powinien wykonać wyszukiwanie niejawne. Dzieje się tak naprawdę, jeśli umieściłem niejawną konwersję w treści "MyExample" lub gdy umieściłem definicję 'class Wrp' poza' Base', jak określono w pytaniu. Nie rozumiem, dlaczego te rozwiązania działają, a oryginalny przykład nie. – perovic

5

Jedną kwestią jest charakter call-by-name argumentu w def ||(wy: =>Wrp[Boolean])
jeśli rewite go def ||(wy: Wrp[Boolean]) działa

ale zgadzam się, że to dziwne, że to działa, jeśli poruszać WRP lub BooleanOpsCls! Zamierzone lub błąd domyślnej rozdzielczości?

+0

Dobrze, mogę potwierdzić, że to działa. Dzięki :) Pozostaje pytanie, co dokładnie dzieje się w oryginalnym przykładzie, również w odniesieniu do dwóch proponowanych obejść. – perovic

+1

@perovic Właśnie idea w kontekście @cbastin odpowiedź: Zakładam, że ze względu na naturę wywołania po nazwie argument niejawne wyszukiwanie odbywa się inaczej, ponieważ '=> Wrp [Boolean]' jest blokiem kodu zamiast typu . Z tego powodu kompilator nie uważa, że ​​definicja tej metody ma zastosowanie podczas wyszukiwania niejawnego. Niestety nie mogę tego potwierdzić żadnym wiarygodnym źródłem, więc jest to całkowicie dzikie domysły. Ale biorąc pod uwagę, że zachowanie jest dziwne i zależy od pozycjonowania "Wrp", wygląda na błąd i myślę, że warto go opublikować jako bilet jira dla zespołu kompilatorów? – Archeg

+0

@Archeg Dziękuję za poradę. Wygląda na to, że wiele elementów i reguł wchodzi w interakcje. Wciąż nie jestem pewien, czy to błąd, więc poczekam jeszcze trochę w nadziei, że ktoś będzie w stanie podać pełne wyjaśnienie. – perovic

1

Problemem jest to, że nie istnieje jedna klasa o nazwie Wrp (zignorować T za chwilę) - Base nie definiuje Wrp lecz definiuje nazwie podklasę każdy beton klasa, która rozszerza Base. Implikacje to także czerwony śledź. Błąd, który jest gratisowy, to wzmianka o MyExample.this.Wrp - pamiętaj, że nie ma takiej klasy, nawet jeśli MyExample - val x = new MyExample miałaby typ Object with MyExample.

+0

Dziękuję za odpowiedź Rob.W rzeczywistym kodzie "Wrp" jest typem abstrakcyjnym, uproścłem to tutaj. Ale nadal przykład działa, jeśli argument "Wrp [Boolean]" operatora '||' jest wartością wywołania lub jeśli wstawię niejawną konwersję (zarówno "niejawną def", jak i 'klasę ') wewnątrz cechy MyExample . Czy wiesz, jak to się ma do twojej odpowiedzi? – perovic

+0

Nie jestem pewien ... może mógłbyś dodać dodatkowy, alternatywny minimalny przykład? –