2016-11-09 8 views
8

Więc jest to dość bezpośredni port this Java question do ScalaUnikanie zbędnych parametry rodzajowe w Scala

Mamy kilka cech, które mają parametry rodzajowe w następujący sposób:

trait Ident { } 

trait Container[I <: Ident] { 
    def foo(id: I): String 
} 

trait Entity[C <: Container[I], I <: Ident] { 
    def container: C 
    def foo(id: I) = container.foo(id) 
} 

To działa, ale jest to mało niezdarny, ponieważ musimy podać typ Tożsamości i typ Pojemnika podczas definiowania podklasy Podmiotu. Podczas gdy w rzeczywistości tylko rodzaj Container wystarczy informacja o typie sama:

class MyIdent extends Ident { } 
class MyContainer extends Container[MyIdent] { } 
class MyEntity extends Entity[MyContainer,MyIdent] { } 
//          ^^^^^^^ shouldn't really be necessary 

Korzystanie typ egzystencjalne eliminuje potrzebę podjęcia Entity dwa parametry ... ale oczywiście nie można odwoływać się do niego później.

trait Entity[C <: Container[I] forSome { type I <: Ident }] { 
    def container: C 
    def foo(id: I) = container.foo(id) 
//   ^^^ complains it has no idea what 'I' is here 
} 

Podobnie przekształcania rzeczy do używania typów członkowskie również nie działa ...

trait Ident { } 

trait Container { 
    type I <: Ident 
    def foo(id: I): String 
} 

trait Entity { 
    type C <: Container 
    def container: C 
    def foo(id: C#I) = container.foo(id) 
//         ^^ type mismatch 
} 

Więc czy ktoś wie, czy jest eleganckie rozwiązanie tego problemu w Scala?

+0

Nie sądzę, że odpowiedź będzie znacznie inna niż wersja Java. Tak naprawdę nie ma sposobu na pominięcie parametru type bez utraty informacji o typie, które by się z nim wiązały. –

+0

Czy jest to opcja, aby zrobić 'container' a' val'? – sepp2k

+0

@MichaelZajac Tak więc w definicji "MyEntity" drugi parametr podany do Entity jest zbędny: po prostu nie ma innego możliwego typu, jakiego można użyć tam, poza "MyIdent", dosłownie wszystko inne da błędy kompilacji. Oczywiście, czy można uniknąć tej redundancji w Scali, to kolejne pytanie :-) –

Odpowiedz

4

Aktualizacjapodane this answer Nie jestem pewien, czy to powinno być uznane za błąd, czy nie

pan uderzy SI-4377; jeśli zapewniają wyraźny type ascriptions dostaniesz błąd, który Zgaduję tylko naraża że prognozy typu są realizowane z wykorzystaniem existentials:

trait Ident { } 

trait Container { 
    type I <: Ident 
    def foo(id: I): String 
} 

trait Entity { 

    type C <: Container 
    def container: C 
    def foo(id: C#I): String = (container: C).foo(id: C#I) 
    // you will get something like: type mismatch; 
    // [error] found : Entity.this.C#I 
    // [error] required: _3.I where val _3: Entity.this.C 
    // as I said above, see https://issues.scala-lang.org/browse/SI-4377 
} 

Nie jest to mało, aby powiedzieć, że to (bug?) sprawia ogólne programowanie z członkowie typu to koszmar.

Nie jest hack chociaż, który polega na odlew wartości do rzemieślnicza autoreferencyjna typu Alias:

case object Container { 

    type is[C <: Container] = C with Container { 

    type I = C#I 
    // same for all other type members, if any 
    } 

    def is[C <: Container](c: C): is[C] = c.asInstanceOf[is[C]] 
} 

teraz używać go i Entity kompiluje:

trait Entity { 

    type C <: Container 
    def container: C 
    def foo(id: C#I): String = Container.is(container).foo(id) 
    // compiles! 
} 

Jest to oczywiście niebezpieczne i z zasady jest bezpieczne tylko wtedy, gdy C i wszystkie jego elementy typu są powiązane z typem nie abstrakcyjnym w punkt, w którym zostanie użyty; zauważ, że nie zawsze tak będzie, ponieważ Scala pozwala ci pozostawić "niezdefiniowanych" członków typu:

case object funnyContainer extends Container { 

    // I'm forced to implement `foo`, but *not* the `C` type member 
    def foo(id: I): String = "hi scalac!" 
} 
+1

Dziękuję, to działa.Cieszę się, że miałem dobry pomysł, ale po prostu trafiłem na błąd :-) –

Powiązane problemy