2014-09-29 16 views
8

Mam zdefiniowane dwie funkcje generyczneJak wywołać wieloznaczną funkcję ogólną w Swift?

func job<T: Comparable>(x: T) { 
    println("1") 
} 

func job<T: Hashable>(x: T) { 
    println("2") 
} 

i gdy próbuję zadzwonić do jednego z nich, na przykład z:

let myInt: Int = 1 // Explicit Int just for clarity of the example 
job(myInt) 

oczywiście Swift narzeka i zgłasza błąd
Niejednoznaczne stosowanie "pracy" co jest zrozumiałe, ponieważ nie jest jasne, czy chcę użyć modelu Porównywalne lub Hashable (Int zgodny zarówno z nich)

Czy istnieje sposób mogę schować kompilator, który z nich chcę użyć?

+1

Czy "praca (myInt as Hashable)" działa? –

+1

Nie :(Otrzymuję 2 błędy: "Protokół" Hashable "może być użyty tylko jako ogólne ograniczenie, ponieważ ma własne lub skojarzone wymagania typu" I "Hashable" typu "nie jest zgodny z protokołem" Porównywalny' (to jeden brzmi dziwnie :)) –

+0

FYI na najnowszą szybką notatkę te dwie świetne odpowiedzi ... http://stackoverflow.com/a/39836054/294884 ... http://stackoverflow.com/a/39835658/294884 – Fattie

Odpowiedz

10

Jest to dwuznaczne, ponieważ Int jest zarówno Hashable i Comparable, a żaden z tych dwóch protokołów nie jest w tej samej hierarchii. (Można wyświetlić Intprotocol hierarchy on Swifter.)

func f<T: Hashable>(t: T) { 
    println("Hashable: \(t)") 
} 
func f<T: Comparable>(t: T) { 
    println("Comparable: \(t)") 
} 

let number = 5 
f(number) 
// error: ambiguous use of 'f' 

Nie można jednoznacznie powiedzieć to, jaką funkcję zadzwonić z powodu związanych z nimi wymagań typ każdego protokołu, ale co może nie jest zdefiniowanie trzeciej funkcji :

func f<T: Comparable where T: Hashable>(t: T) { 
    println("Both Hashable & Comparable: \(t)") 
} 
f(number) 
// Both Hashable & Comparable: 5 

ten sposób Swift implements operator ..<, które w przeciwnym razie byłyby niejednoznaczne dla typów, które realizują zarówno Comparable i ForwardIndexType.


Aby rozwinąć trochę dalej, tu jest spojrzenie na to, co rozumie przez „nie można jednoznacznie stwierdzić, które funkcjonują go nazwać, z powodu związanych z nimi wymagań typ każdego protokołu.” Protokoły mogą być wykorzystane jako typy, jak opisano w książce chapter on Protocols Swift:

protocol RandomNumberGenerator { 
    func random() -> Double 
} 

class Dice { 
    let generator: RandomNumberGenerator 
    // ... 
} 

W tym przykładzie właściwość generator może być dowolnego typu, który jest zgodny z RandomNumberGenerator - podobnie jak id<ProtocolName> jest stosowany w Objective-C. Jednak protokoły mogą być używane jako typy, gdy nie zawierają skojarzonego typu lub odniesienia Self w ich deklaracji. To niestety wyklucza prawie każdy wbudowany typ w Swift, w tym Hashable i Comparable.

Hashable dziedziczy z Equatable, które odniesienia Self przy określaniu operatora ==:

func ==(lhs: Self, rhs: Self) -> Bool 

i Comparable czyni to samo jej operacji:

func <=(lhs: Self, rhs: Self) -> Bool 
// similar definitions for <, >, and >= 

Protokoły te mogą jedyniesłużyć jako ogólne ograniczenia i nie są używane jako typ podczas deklarowania zmiennej. (O ile mogę powiedzieć, że jest to nieudokumentowane, ale można je wykryć za pomocą komunikatów o błędach.)

Dwa protokoły nie mają ograniczenie są Printable i BooleanType, więc możemy patrzeć na to, jak one działają. Bool to jedyny wbudowany typ, który jest zgodny z BooleanType i jest również Printable, więc będzie to nasz typ testu. Oto nasze ogólne funkcje p() i zmienna t - zauważyć, że tak jak wcześniej, nie możemy po prostu wywołać funkcję z t:

func p<T: Printable>(t: T) { 
    println("Printable: \(t)") 
} 
func p<T: BooleanType>(t: T) { 
    println("BooleanType: \(t)") 
} 

let t: Bool = true 
p(t) 
// error: Ambiguous use of 'p' 

Zamiast tego, musimy oddać (uskok?) t do określonego protokołu za pomocą słowa kluczowego as i wywołać określoną funkcję Generic tamtędy:

p(t as Printable) 
// Printable: true 

p(t as BooleanType) 
// BooleanType: true 

tak długo, jak mamy protokół kwalifikacyjny, możemy wybrać, który wariant metody rodzajowe zadzwonić.

+1

Aby wyjaśnić, metody "porównywalne" i "wymierne" miały właśnie na celu wykazanie problemu. Nie spodziewam się ich potrzebować, ale chciałem zrozumieć, jak ograniczony jestem w przyszłości. Teraz nie jestem pewien, co masz na myśli, mówiąc: "Nie możesz jednoznacznie powiedzieć, która funkcja zadzwonić, ** ze względu na skojarzone wymagania dotyczące typu każdego protokołu **". Czy jest tu jakiś zasadniczy problem z możliwością dostarczenia kompilatorowi podpowiedzi dotyczącej metody, której należy użyć? Czy uważasz, że możemy spodziewać się takiej funkcji w przyszłości? –

+0

Czy mówienie kompilatorowi jakiej funkcji należy użyć w przeciwieństwie do projektowania ogólnego programowania (ogólnie, nie tylko w Swift)? Niezależnie od tego, jeśli istnieje funkcja, którą chcesz zobaczyć (lub przynajmniej nie należy się jej spodziewać) w przyszłości, prawdopodobnie najlepiej jest [zgłosić błąd] (http://bugreport.apple.com). – rickster

+0

@BartekChlebek: Dodano trochę (w porządku, dużo) wyjaśnień na ten temat. Swift z pewnością się zmienia, ale niekoniecznie widzę, że musi się zmienić. Ten rodzaj konfliktu wydaje się stosunkowo rzadki, nie? –