Mam pewne problemy motywujące użycie klas typów w Scali, gdy porówna się z górnymi granicami typów.Jaka jest motywacja dla klas typów w Scali?
Rozważmy następujący kod:
case class NumList[T <: Complex](xs: Complex*) {
def sum = (xs fold new Complex(0, 0))(_ + _)
def map[U <: Complex](f: Complex => U): NumList[U] = NumList(xs.map(f): _*)
override def toString = "[" + xs.mkString(", ") + "]"
}
case class GenList[T](xs: T*) {
def sum(implicit num: Numeric[T]) = xs.sum
def map[U](f: T => U) = GenList(xs.map(f): _*)
override def toString = "[" + xs.mkString(", ") + "]"
}
val r = new Real(2)
val n = new Natural(10)
val comps = NumList(r, n, r, n)
println(comps)
println("sum: " + comps.sum)
println("sum * 2: " + comps.map(x => x + x).sum)
val comps2 = GenList(4, 3.0, 10l, 3d)
println(comps2)
println("sum: " + comps2.sum)
println("sum * 2: " + comps2.map(_ * 2).sum)
Chociaż te dwie listy rozwiązać podobne problemy, jeden używa numeryczną typu klasy, a drugi wykorzystuje górną granicę parametru type. Rozumiem różnice techniczne dość dobrze, ale ciężko jest mi dotrzeć do podstawowej motywacji klas typu. Najlepszą motywacją, jaką znalazłem do tej pory, jest:
Podczas gdy tworzenie podklas lub implementowanie interfejsów pozwala na wykonywanie w większości tych samych projektów, klasy typów pozwalają na określenie cech typu na podstawie metody, podczas gdy klasa ogólna z typem T
i górna granica U
ogranicza wszędzie, gdzie jest używana, ograniczenie T
. Mając to na uwadze, klasy typów zapewniają dokładniejszą kontrolę nad cechami T w klasach ogólnych.
Czy są jakieś bardzo wyraźne przykłady motywujące wzór?
To jest dokładne uzasadnienie. Ogólnie, Klasy typów mogą być bardziej zwięzłe, niż w Scali. Sposób, w jaki są one kodowane, pozwala na ich użycie z dużo większą elastycznością, wtedy można to zrobić w coś w rodzaju waniliowego Haskella, ale implementacja Haskella jest dużo bardziej zwięzła. Innym dobrym przykładem jest dodanie do hierarchii czegoś w rodzaju Serializacji, dodając interfejs do górnej części hierarchii, która rozbije wszystkie stare klasy. Korzystanie z klas typów pozwala na stopniową implementację funkcjonalności, a konkretnie tylko na potrzebnych typach. – jroesch
Tak jak powiedział @jroesch, warto podkreślić fakt, że typografia jest zaimplementowana w scala jako wzorzec (to znaczy nie jest to funkcja językowa), ale pojęcie pochodzi z innych języków (takich jak haskell), w których funkcja jest osadzona w sam język. E.g nie jest językiem OO, nie ma mechanizmu dziedziczenia, więc funkcja typeclass służy do uzyskiwania wspólnej funkcji poprzez implementację funkcji niestandardowych na różnych typach danych.Dlatego też typografia może wydawać się zbędna w scala, gdzie występuje nakładanie się funkcji z dziedziczeniem obiektów. –
Bardzo dobrze. Zostawiłem to na jakiś czas i wróciłem do pytania na własną rękę, starając się to zmotywować. Wymyśliłem bardzo podobny argument. Miły! – Felix