2009-02-26 26 views
10

Chciałbym wiedzieć, jak działają typy członków w Scali i jak powinnam powiązać typy.Parametry typu a typy członków w Scali

Jednym ze sposobów jest uczynienie skojarzonego typu parametrem typu. Zaletami tego podejścia jest to, że mogę przepisać wariancję typu i mogę być pewien, że podtyp nie zmienia typu. Wady polegają na tym, że nie mogę wywnioskować parametru typu z typu w funkcji.

Drugie podejście polega na uczynieniu powiązanego typu członem drugiego typu, który ma problem z tym, że nie mogę określić granic powiązanych typów podtypów i dlatego nie mogę użyć typu w parametrach funkcji (jeśli x x x o * nie może być w żadnym związku z Xt)

Konkretnym przykładem może być:

mam cechę dla DFAS (może być bez parametru typu)

trait DFA[S] { /* S is the type of the symbols in the alphabet */ 
    trait State { def next(x : S); } 
    /* final type Sigma = S */ 
} 

i chcę utworzyć funkcję do biegania to DFA nad sekwencji wejściowej i chcę

  • funkcja musi brać niczego <% Seq[alphabet-type-of-the-dfa] jak typ sekwencji wejściowych
  • rozmówca funkcja nie musi określić parametry typu, wszystko musi być wyprowadzone
  • ja podobnie jak funkcja, która ma być wywołana z konkretnym typem DFA (ale jeśli istnieje rozwiązanie, w którym funkcja nie ma parametru typu dla DFA, jest OK), to typy alfabetu muszą być nieograniczone (tj. musi istnieć DFA na Char, jak również jeszcze nieznanych klasy zdefiniowanej przez użytkownika)
  • DFAS z różnymi rodzajami alfabetu nie są podtypy

Próbowałem to:

def runDFA[S, D <: DFA[S], SQ <% Seq[S]](d : D)(seq : SQ) = .... 

to działa , z wyjątkiem typu S nie jest tu wywnioskowana, więc muszę napisać listę parametrów całego typu na każdej stronie wywołania.

def runDFA[D <: DFA[S] forSome { type S }, SQ <% Seq[D#Sigma]](... same as above 

to nie działa (nieprawidłowy okrągły odniesienie do typu D ??? (co to jest?))

ja również usunięty parametr typu, stworzony abstrakcyjny typ Sigma i próbował wiązanie tego typu w konkretnych klasach. runDFA wyglądałby

def runDFA[D <: DFA, SQ <% Seq[D#Sigma]](... same as above 

ale to nieuchronnie prowadzi do problemów, takich jak "niedopasowanie typu: oczekuje dfa.Sigma, dostał D#Sigma"

Jakieś pomysły? Wskaźniki?

Edit:

W odpowiedzi wskazują brak jest prosty sposób to zrobić, ktoś mógłby opracować więcej o tym, dlaczego jest to niemożliwe, a co będzie musiał być zmieniony tak to działało?

powodów, dla których chcą runDFA RO być bezpłatna funkcja (nie metoda) jest to, że chcę inne podobne funkcje, jak na automatach minimalizacji, regularnych operacji językowych, NFA-to-DFA konwersji, język faktoryzacji itp i spełniające wszystkie to wewnątrz jednej klasy jest sprzeczne z prawie każdą zasadą projektowania OO.

+0

Wow, myślałem, że masz na myśli to: http://www.huygens-fokker.org/scala/ – MusiGenesis

Odpowiedz

3

Po pierwsze, nie trzeba SQ parametryzacji <% Seq [ S]. Napisz parametr metody jako Seq [S]. Jeśli SQ <% Seq [S], wówczas dowolna jego instancja jest niejawnie możliwa do zamiany na Seq [S] (czyli co oznacza <%), więc gdy zostanie przekazany jako Seq [S], kompilator automatycznie wstawi konwersję.

Dodatkowo, co Jorge powiedział o parametrach typu na D i uczynieniu go metodą zatrzymania DFA. Ze względu na sposób pracy wewnętrznych klas w Scali, zdecydowanie zalecałbym umieszczanie runDFA na DFA. Dopóki nie będzie zależało od typowania ścieżek, radzenie sobie z wewnętrznymi klasami niektórych zewnętrznych klas może być trochę uciążliwe.

Więc teraz masz

trait DFA[S]{ 
    ... 

    def runDFA(seq : Seq[S]) = ... 
} 

I runDFA jest nagle dość łatwo wywnioskować na parametry typu: To nie ma żadnego.

3

Wnioskowanie typu Scala czasami pozostawia wiele do życzenia.

Czy istnieje jakiś powód, dla którego nie można zastosować tej metody w swojej charakterystyce DFA?

def run[SQ <% Seq[S]](seq: SQ) 

Jeśli nie potrzebują D param później, można także spróbować określające metodę bez niego:

def runDFA[S, SQ <% Seq[S]](d: DFA[S])(seq: SQ) = ... 
0

Kilka przydatnych informacji o tym, jak dwaj różni:

Z bezkształtnej guide:

bez parametrów typu nie można dokonać typów zależnych, na przykład

trait Generic[A] { 
    type Repr 
    def to(value: A): Repr 
    def from(value: Repr): A 
} 

import shapeless.Generic 
def getRepr[A](value: A)(implicit gen: Generic[A]) = 
    gen.to(value) 

tutaj typ zwracany przez to zależy od typu wejścia A (ponieważ dostarczony niejawny zależy od A):

case class Vec(x: Int, y: Int) 
case class Rect(origin: Vec, size: Vec) 
getRepr(Vec(1, 2)) 
// res1: shapeless.::[Int,shapeless.::[Int,shapeless.HNil]] = 1 :: 2 :: 
    HNil 
getRepr(Rect(Vec(0, 0), Vec(5, 5))) 
// res2: shapeless.::[Vec,shapeless.::[Vec,shapeless.HNil]] = Vec(0,0) 
    :: Vec(5,5) :: HNil 

bez członków typu byłoby to niemożliwe:

trait Generic2[A, Repr] 
def getRepr2[A, R](value: A)(implicit generic: Generic2[A, R]): R = 
    ??? 

Mielibyśmy przekazać żądaną wartość Repr do getRepr jako parametr typu, effec Vely dokonywania getRepr bezużyteczne. Wynika to z tego, że parametry typu są użyteczne jako "dane wejściowe", a elementy typu " " są użyteczne jako "wyjścia".

Szczegółowe informacje znajdują się w bezkształtnym guide.