2015-11-23 25 views
7

Siamo spiacenti per il titolo generico, è difficile descrivere il problema senza esempi.Cosa sta succedendo con questa funzione generica?

Supponiamo che io definire la seguente funzione generica che è costretto a Equatable tipi:

func test<T: Equatable>(expect expected: T, run:() -> T) { 
    let value = run() 
    if value == expected { 
     print("OK") 
    } else { 
     print("Expected: \(expected), Actual: \(value)") 
    } 
} 

Ecco un esempio di utilizzo di tale funzione:

test(expect: 100) { 10 * 10 } // prints "OK" 
test(expect: 1000) { 10 * 10 } // prints "Expected: 1000, Actual: 100" 

E, naturalmente, posso conservare il valore invece di usare letterali:

let e = 100 
test(expect: e) { e } // prints "OK" 

Fin qui tutto bene, tutto funziona come previsto (nessun gioco di parole).

Ora proviamo questo con una matrice:

test(expect: [1, 2]) { [1, 2] } // prints "OK" 

Ancora una volta, le cose funzionano.

Ma ora proviamo questo:

let a = [1, 2] 
test(expect: a) { a } // error: cannot convert value of type '() -> [Int]' to expected argument type '() -> _' 

Quindi la domanda che mi sono state costruendo a è: Perché non fa questo lavoro?

giochi deduce correttamente il tipo di a per essere [Int], quindi da dove l'aspettativa di () -> _ viene?

Cercando un gruppo di variazioni dell'ultimo esempio:

test(expect: a) { return a } 
test(expect: a) { return a as [Int] } 
test(expect: a as [Int]) { return a as [Int] } 
test(expect: [1, 2]) { a } 
test(expect: [1, 2] as [Int]) { a } 

comportano tutte lo stesso problema. Per qualche ragione, Swift sembra pensare che la funzione si aspetta () -> _.

quindi forse è solo perché gli array non sono Equatable, ma questo funziona:

let a = [1, 2] 
[1, 2] == [1, 2] 
a == a 

Credevo di aver capito generici abbastanza bene, e sto completamente perplesso da questo. Si tratta di un bug in Swift o di un bug nella mia definizione di test()? L'obiettivo può essere raggiunto?

La soluzione

Grazie a @ risposta di Sulthan di seguito, sono stato in grado di scrivere un'altra versione di questa funzione per gestire il caso array (e qualsiasi SequenceType per questo):

public func test<T: SequenceType where T.Generator.Element: Equatable>(expect expected: T, run:() -> T) { 
    let result = run() 
    // Note: zip() will stop at the shorter array, so this implementation isn't correct, don't use it (it will incorrectly end up saying [1] == [1,2]). This code is just here to demonstrate the function's generic constraint. 
    let eq = zip(expected, result).filter(!=).isEmpty 
    if eq { 
     print("OK") 
    } else { 
     print("Expected: \(expected), Actual: \(result)") 
    } 
} 

let a: [Int] = [1, 2] 
test(expect: [1,2]) { a } // prints "OK" 
test(expect: [1,3]) { a } // prints "Expected: [1, 3], Actual: [1, 2]" 
+2

La risposta è la stessa di http://stackoverflow.com/a/33732669/669586 ma non sono sicuro di doverlo chiudere come duplicato. – Sulthan

+1

@Sulthan È sicuramente la stessa domanda alla radice, ma il fatto che farlo attraverso una funzione offusca il problema un po 'penso che sia degno di stare in piedi da solo. Inoltre, una parte della domanda era come fare questo lavoro, che anche la modifica alla domanda si rivolge. – vopilif

+0

Come nota a margine sulla mia "soluzione" sopra. Probabilmente è una cattiva idea limitare l'implementazione di 'test()' a un 'SequenceType' perché è valido avere sequenze infinite, quindi non si vuole provare a ripetere su tutte loro. 'CollectionType' sarebbe più appropriato. – vopilif

risposta

5

Array don sono automaticamente conformi a Equatable, anche se i loro valori sono Equatable. Tuttavia, quando si utilizza direttamente un array letterale, il compilatore tenta di far corrispondere il tipo e converte l'array in un NSArray conforme a Equatable.

+0

Questo è tutto! Wow, questo mi ha infastidito per un po 'di tempo! Grazie! – vopilif

+1

In realtà, giocarci un po 'di più .. Sei sicuro che lo stia convertendo in un NSArray? Se rimuovo 'import Foundation', non posso più usare NSArray, ma' [1,2,3] == [1,2,3] 'funziona ancora, quindi qualcosa di diverso deve succedere. – vopilif

+1

@vopilif C'è '==' definito per Array con 'Elemento: Equitable'. Ciò non significa che Array implementa "Equitable". – Sulthan

Problemi correlati