2014-09-29 12 views
8

Ho definito due funzioni genericheCome chiamare la funzione generica ambigua in Swift?

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

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

e quando provo a chiamare uno di loro, ad esempio con:

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

naturalmente Swift si lamenta e genera un errore
uso ambiguo di 'lavoro'
che è comprensibile, perché non è chiaro se voglio utilizzare lo comparabile uno o hashable (Int conforme ad entrambi)

C'è un senso che posso accennare al compilatore quale voglio usare?

+1

Il lavoro '(myInt as Hashable)' funziona? –

+1

No :(Ottengo 2 errori: 'Protocollo 'Hashable' può essere usato solo come un vincolo generico perché ha i requisiti di tipo Self o assosiated' AND 'Type' Hashable 'non è conforme al protocollo' Comparable'' (questo uno suona strano :)) –

+0

FYI per l'ultima nota rapida queste due grandi risposte ... http://stackoverflow.com/a/39836054/294884 ... http://stackoverflow.com/a/39835658/294884 – Fattie

risposta

10

Questo è ambiguo perché Int è sia Hashable sia Comparable e nessuno di questi due protocolli si trova nella stessa gerarchia. (È possibile visualizzare il 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' 

Non si può dire esplicitamente quale funzione chiamare, a causa dei requisiti di tipo associato di ogni protocollo, ma quello che si può fare è definire una terza funzione :

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

Così Swift implements il ..< operatore, che altrimenti sarebbe ambiguo per i tipi che implementano sia Comparable e ForwardIndexType.


Per espandere un po 'più avanti, ecco uno sguardo a ciò che intende per "non si può dire che in modo esplicito quale funzione chiamare, a causa dei requisiti di tipo associato di ogni protocollo." Protocolli possono essere usati come tipi, come descritto nel libro Swift chapter on Protocols:

protocol RandomNumberGenerator { 
    func random() -> Double 
} 

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

In questo esempio, la proprietà generatore può essere qualsiasi tipo conforme a RandomNumberGenerator - simile a come id<ProtocolName> viene utilizzato in Objective-C. Tuttavia, i protocolli possono solo essere utilizzati come tipi quando non includono un tipo o riferimento Self nella dichiarazione. Questo purtroppo esclude quasi tutti i tipi di built-in in Swift, tra cui Hashable e Comparable.

Hashable eredita da Equatable, che fa riferimento Self per la definizione del == esercente:

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

e Comparable fa lo stesso con i suoi gestori:

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

Questi protocolli possono solo essere utilizzati come vincoli generici e non usati come tipo quando si dichiara una variabile. (Per quanto posso dire questo non è documentato, ma è individuabile attraverso i messaggi di errore.)

Due protocolli che Non hanno tale restrizione sono Printable e BooleanType, in modo che possiamo osservare come funzionano. Bool è l'unico tipo integrato conforme a BooleanType ed è anche Printable, quindi questo sarà il nostro tipo di test. Ecco le nostre funzioni generiche p() e la variabile t - notare che, come prima, non possiamo semplicemente chiamare la funzione con 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' 

Invece, abbiamo bisogno di lanciare (upcast?) t ad un particolare protocollo utilizzando la parola chiave as, e chiamare una particolare funzione generica in questo modo:

p(t as Printable) 
// Printable: true 

p(t as BooleanType) 
// BooleanType: true 

Quindi, fintanto che abbiamo un protocollo di qualifica, siamo in grado di scegliere quale variante del metodo generico da chiamare.

+1

Per chiarire, i metodi 'Comparable' e' Hashable' erano solo per dimostrare il problema. Non mi aspetto di averne bisogno, ma volevo capire quanto potrei essere limitato in futuro. Ora, non sono sicuro di cosa intendi per "Non puoi dire esplicitamente quale funzione chiamare, ** a causa dei requisiti di tipo associati di ciascun protocollo **". C'è qualche problema fondamentale qui con essere in grado di fornire al compilatore un suggerimento su quale metodo usare? Pensi che possiamo aspettarci una tale funzione in futuro? –

+0

Non sta dicendo al compilatore quale funzione utilizzare in opposizione al progetto di programmazione generica (in generale, non solo in Swift)? Indipendentemente da ciò, se c'è una funzione che vuoi vedere (o almeno che ti viene detto di non aspettarti) in futuro, è probabilmente meglio [archiviare un bug] (http://bugreport.apple.com). – rickster

+0

@BartekChlebek: aggiunto un po '(ok, molto) di spiegazione su questo punto. Swift è certamente in evoluzione, ma non vedo necessariamente che debba cambiare. Questo tipo di conflitto sembra relativamente raro, no? –

Problemi correlati