2010-07-03 35 views
19

W jakich sytuacjach preferowane są typy abstrakcyjne nad parametrami typu?Typy abstrakcyjne a parametry typu

+1

Czy nie jest to już omówione na http://stackoverflow.com/questions/1154571/scala-abstract-types-vs-generics/1154727#1154727? – VonC

+1

@VonC: Widziałem tę odpowiedź, ale nie uznałem jej za satysfakcjonującą. –

+0

Próbuję zilustrować to poniżej, z niedawnym wpisem na blogu od Jessego EICHARA. – VonC

Odpowiedz

16

Aby dodać do moich previous answer on Abstract type vs. parameters, masz również JESSE EICHAR's recent blog post (2010, 3 maja) podkreślając kilka kluczowych różnic:

trait C1[A] { 
    def get : A 
    def doit(a:A):A 
} 
trait C2 { 
    type A 
    def get : A 
    def doit(a:A):A 
} 

W C2 przypadku parametr jest „pochowany” (jako wewnętrzny abstrakcyjny rodzaj).
(z wyjątkiem, jak retronim stawia go, nie jest rzeczywiście pochowany, patrz niżej)

Podczas gdy z typu rodzajowego parametr jest wyraźnie wymienione, pomagając inne wyrażenia wiedzieć, jakiego rodzaju mają się stosować


Tak (C1: parametr):

//compiles 
def p(c:C1[Int]) = c.doit(c.get) 

To kompiluje, ale naraża się wyraźnie 'A' typ, którego chcesz użyć.

i (C2: Abstract typ):

// doesn't compile 
def p2(c:C2) = c.doit(c.get) 
<console>:6: error: illegal dependent method type 
     def p2(c:C2) = c.doit(c.get) 
      ^

To nie kompiluje ponieważ „A” nigdy nie jest wymienione w definicji P2, tak doit nie wie, w rodzaju kompilacji to, co ma, aby powrócić .


Podczas korzystania abstrakcyjny typ i chcąc uniknąć wszelkich „typ wycieku” do interfejsu (czyli chcąc narażać co A 'faktycznie jest), można określić typ bardzo ogólny jako powrót do p2 :

// compiles because the internals of C2 does not leak out 
def p(c:C2):Unit = c.doit(c.get) 

Albo można "naprawić" tego typu bezpośrednio w doit funkcję:
def doit(a:A):Int zamiast def doit(a:A):A, co oznacza:
def p2(c:C2) = c.doit(c.get) skompiluje (nawet jeśli p2 nie wspomina o żadnych typ zwracany)


Finally (retronym „s komentarz) można określić A jawnie przez rafinacji C2 abstrakcyjnego parametru:

scala> def p2(c:C2 { type A = Int }): Int = c.doit(c.get) 
p2: (c: C2{type A = Int})Int 

albo przez dodawanie parametru typu (i dopracowywanie typu abstrakcyjnego C2 za pomocą!)

scala> def p2[X](c:C2 { type A = X }): X = c.doit(c.get) 
p2: [X](c: C2{type A = X})X 

Więc abstrakcyjne są zalecane:

  • Gdy chcesz ukryć dokładnej definicji członka typu z kodu klienckiego, użyj abstrakcyjny typ jak w C2 (ale być nieufny wobec definicji funkcji przy użyciu C2)
  • Gdy chcesz zastąpić typ współrzędnie w podklasach C2, użyj abstrakcyjny typ (z ograniczonego typu abstrakcji)
  • Gdy chcesz mieszać w definicjach tych C2 typów poprzez cech, użyj abstrakcyjny typ (nie trzeba „A” do czynienia podczas mieszania z C2 klasa: wymieszać tylko C2)

do reszty, gdzie proste typ instancji potrzeba, użyć parametrów.
(jeśli wiesz, że bez rozszerzenia będą potrzebne, ale nadal trzeba obsługiwać kilka rodzajów: to jakie typy parametrów są za)


retronym dodaje:

The główne różnice

  • wariancji: C2 może być tylko niezmienna w A,
  • sposób, że członkowie typu mogą być selektywnie nadpisane w podtyp (podczas gdy parametry muszą być zapisane redeclared i przekazywana nadtypu)

(jak illustrating here:

trait T1 { 
    type t 
    val v: t 
} 
trait T2 extends T1 { 
    type t <: SomeType1 
} 
trait T3 extends T2 { 
    type t <: SomeType2 // where SomeType2 <: SomeType1 
} 
class C extends T3 { 
    type t = Concrete // where Concrete <: SomeType2 
    val v = new Concrete(...) 
} 

)

+6

To nie jest naprawdę pochowany: 'def p2 (c: C2 {typ A = Int}): Int = c.doit (c.get)'. Lub: def p2 [X] (c: C2 {typ A = X}): X = c.doit (c.get) '. Głównymi różnicami są wariancja ("C2" może być tylko niezmienna w "A") oraz sposób, w jaki członkowie typu mogą być selektywnie nadpisywane w podtypie, podczas gdy parametry typu muszą być redeclared i przekazywane do nadtypu. – retronym

+1

@retronym: * "Główne różnice to wariancja ... i sposób, w jaki członkowie typu mogą zostać selektywnie zmienione" * Czy to oznacza, że ​​wszystko, co jest możliwe do zrobienia z jednym z nich, można zrobić z drugim? Z wyjątkiem dwóch aspektów, o których wspomniałeś. Jedyną różnicą jest składnia? – Lii