2013-09-01 13 views
5

Przeczytałem kilka artykułów wyrażających, że w celu uzyskania polimorfizmu związanego z F w Scali należy używać typów abstrakcyjnych. Ma to na celu przede wszystkim złagodzenie problemów z wnioskami typu, ale także usunięcie kwadratowego wzrostu, jaki parametry wydają się wprowadzać przy definiowaniu typów rekursywnych.F-Bounded polimorfizm z typami abstrakcyjnymi w Scali

Są one zdefiniowane jako tak:

trait EventSourced[E] { 
    self => 

    type FBound <: EventSourced[E] { type FBound <: self.FBound } 

    def apply(event: E): FBound 
} 

to jednak wydaje się wprowadzenie dwóch kwestii:

1) Za każdym razem gdy użytkownik chce odwołać obiekt tego typu, muszą one również dotyczyć parametr typu FBound. To czuje się jak zapach kod:

def mapToSomething[ES <: EventSourced[E], E](eventSourced: ES#FBound): Something[ES, E] = ... 

2) kompilator jest teraz w stanie wywnioskować parametrów typu metod, takich jak powyższe, w przypadku braku z komunikatem:

Type mismatch, expected: NotInferredES#FBound, actual: MyImpl#FBound 

Czy ktoś tam przy użyciu udane wdrożenie w swoim rozwiązaniu polimorfizmu związanego z atomem f, w którym kompilator wciąż jest w stanie wnioskować o typach?

+0

Scala zbiory biblioteki korzysta F-ograniczony polimorfizm pomyślnie bez problemów. Używa parametrów typu, a nie członków typu, możesz wypróbować rozwiązanie oparte na tym. – wingedsubmariner

+0

Proszę podać przykład, na który należy zwrócić uwagę, tj. Które części biblioteki to robią? –

+0

Zobacz [List] (http://www.scala-lang.org/api/current/index.html#scala.collection.immutable.List) w standardowej bibliotece. Zwróć uwagę na polimorfizm związany z F używany w dziedziczeniu GenericTraversableTemplate i LinearSeqOptimized. – wingedsubmariner

Odpowiedz

3

mam ponieważ zrozumiał, że polimorfizm f ograniczona należy unikać w większości przypadków - albo raczej - tam zwykle alternatywny projekt, który należy wybrać. Aby zrozumieć, jak go uniknąć, musimy najpierw wiedzieć, co sprawia, że ​​wymaga to:

polimorfizm F-ograniczony występuje wtedy, gdy typ spodziewa ważne interfejs zmienia zostać wprowadzony w rodzaju pochodnych.

to uniknąć komponowania oczekiwanych obszary zmian zamiast próbuje wspierać ich poprzez dziedziczenie. To rzeczywiście wraca do Gangu Czterech wzorców projektowych:

Favor 'skład obiektu' nad '' klasy dziedziczenia

- (Gang of Four, 1995)

Na przykład:

trait Vehicle[V <: Vehicle[V, W], W] { 
    def replaceWheels(wheels: W): V 
} 

staje:

trait Vehicle[T, W] { 
    val vehicleType: T 
    def replaceWheels(wheels: W): Vehicle[T, W] 
} 

Tutaj "oczekiwaną zmianą" jest typ pojazdu (np. Bike, Car, Lorry). Poprzedni przykład zakładał, że zostanie on dodany poprzez dziedziczenie, wymagając typu ograniczonego do f, który uniemożliwiłby dowolnej funkcji przy użyciu Pojazdu. Nowa metoda, która używa kompozycji, nie wykazuje tego problemu.

Patrz: https://github.com/ljwagerfield/scala-type-inference/blob/master/README.md#avoiding-f-bounded-polymorphism

+0

> Nowa metoda, która używa kompozycji, nie wykazuje tego problemu. Nie do końca prawda. Jeśli chcesz zachować kontrole na poziomie typu, potrzebujesz go w końcu, np. 'VehicleType [V <: VehicleType] – ayvango

-1
+0

W tym przykładzie zastosowano parametry typu, co powoduje problemy z wnioskami. W moim przykładzie typów dla następującej definicji metody nie można wywnioskować w witrynie wywołania: def doSomething [ES <: EventSourced [ES, E], E] (that: ES) –

+0

W przykładach z Twittera lub biblioteki Scala parametr typu związanego F jest związany z samą klasą, a nie z metodami. Polimorfizm ograniczony do F pozwala metodom odwoływać się do typu "this", dokładnie takiego typu, jaki kończy się będąc w podklasach. Weźmy na przykład definicję 'map' w' TraversableLike' odziedziczoną po 'List',' def map [B, That] (f: A => B) (implicit bf: CanBuildFrom [Repr, B, That]): That '. Repr jest parametrem typu F-Bound i jest List [A] wewnątrz listy, ale kod w TraversableLike można zapisać bez znajomości "List". – wingedsubmariner

+0

Tak, myślę, że ostatecznie muszę ponownie przemyśleć część mojego projektu. Wydaje mi się, że polimorfizm ograniczony do F powinien być stosowany tylko na poziomie definicji typu, gdy dziedziczy się z supertekstu związanego z F. Jednak w moim kodzie jest kilka przypadków, w których jest on używany na poziomie metody ... Czuję, że to musi się zmienić. –

-1

Prawdopodobnie brakuje mi czegoś z następującą implementacją.

trait EventSourced[E] { 
    self => 

    type FBound <: EventSourced[E] { type FBound <: self.FBound } 

    def apply(event: E): FBound 
} 

trait Something[ES, E] 

def mapToSomething[E](
    eventSourced: ES forSome { 
    type ES <: EventSourced[E] 
    }): Something[eventSourced.type, E] = ??? 

class Test extends EventSourced[Boolean] { 
    type FBound = Test 
    def apply(event:Boolean):FBound = ??? 
} 

val x:Test = ??? 

mapToSomething(x)