2010-08-12 14 views
5

Jestem zainteresowany problemem dostosowania określonego typu do bardziej ogólnego typu strukturalnego. Rozważmy następujące przykłady:Uogólniona zgodność z typem strukturalnym w Scali

trait Sup 

trait Sub extends Sup 

type General = { 
    def contra(o: Sub): Unit 
    def co(): Sup 
    def defaults(age: Int): Unit 
    def defaults2(age: Int): Unit 
    def defaults3(first: String): Unit 
} 

trait Specific { 
    def contra(o: Sup): Unit // doesn't conform 
    def co(): Sub // conforms 
    def defaults(age: Int, name: String = ""): Unit // doesn't conform 
    def defaults2(name: String = "", age: Int = 0): Unit // doesn't conform 
    def defaults3(first: String = "", last: String = ""): Unit // doesn't conform 
} 

W każdym z przypadków niezgodnych z wymaganiami, wywołanie metody w General może bezpiecznie zostać rozwiązany w odpowiedni sposób w Specific. Bardziej interesujący praktyczny przykład można znaleźć w this question:

trait Versionable[T] { 
    self: { def copy(version: Int): T } => 
    val version = 0 
    def incrementVersion = copy(version = version + 1) 
} 

case class Customer(name: String, override val version: Int) 
     extends Versionable[Customer] { 
    def changeName(newName: String) = copy(name = newName) 
} 

Tutaj copy metoda Klienta nie jest zgodny z podpisem w Versionable self-type adnotacji. Zauważ jednak, że jeśli dozwolony jest kompilator, można wywołać copy, tak jak jest w Versionable.incrementVersion. Oczywiście, rzeczywisty podpis Klienta w metodzie copy jest zbyt szczegółowy, aby można go było używać w wersji, która może być nieaktualna, ponieważ można podać opcjonalnie parametr name.

Czy istnieją sposoby na obejście tych ograniczeń? Czy istnieją powody, aby taka uogólniona zgodność była złym pomysłem?

+0

Inny praktyczny przykład: http://stackoverflow.com/questions/4410469/refactoring-copy-functionality –

Odpowiedz

1

Jednym problemem jest to, że kiedy czytasz ten kod:

self: { def copy(version: Int): T } 

nie oczekują, że nazwa parametru za znaczące, gdyż muszą być w tym przykładzie:

case class Robot(number: Int, override val version: Int) 
    extends Versionable[Robot] 

EDIT: na innej notatki, dotyczące braku parametru kontrawariancji dla metod, można zrobić:

type General = { val contra: (Sub => Unit) } 
class B { val contra = ((o:Sup) => println(o)) } 
var b:General = new B 
+0

To dobra uwaga. Tego rodzaju uogólniona zgodność prawdopodobnie nie powinna być domyślna (ze względu na wydajność). Wymaganie adnotacji '@ named' w parametrze' version' w typie strukturalnym rozwiązałoby oba problemy, jak sądzę. –