2014-09-05 10 views
6

Si tratta di un follow-up alla domanda: Protocol func returning Self. Il protocollo è il seguente:Swift protocollo e ritorno tipi su funzioni globali

protocol Copyable { 
    init(copy: Self) 
    func copy() -> Self 
} 

I seguenti funziona bene, ma la funzione copy() è esattamente la stessa per ogni implementazione, cioè

func copy() -> Self { 
    return self.dynamicType(copy: self) 
} 

Conformemente a questa http://nshipster.com/swift-default-protocol-implementations/ ho provato un Funz globale

func copy<T : Copyable>(makeCopy: T) -> T { 
    return makeCopy.dynamicType(copy: makeCopy) 
} 

Tuttavia, quando viene chiamato in una classe che implementa il protocollo seguente

protocol Mutatable : Copyable { 
    func mutated() -> Self 
} 

class C : Mutatable { 

    var a = 0 

    required init(_ a: Int) { 
     self.a = a 
    } 

    required init(copy: C) { 
     a = copy.a 
    } 

    func mutated() -> Self { 
     let mutated = copy(self) 

     mutated.a++ 

     return mutated // error: 'C' is not convertible to 'Self' 
    } 

} 

ottengo l'errore come indicato. Quando digito il completamento automatico mutated mostra mutated come (C) e non ho idea di cosa significhi. Ho anche provato ad aggiungere required a func mutated() ma apparentemente required è consentito solo per inits. Qualche modo per farlo funzionare?

risposta

1

Questa domanda ha la stessa forma di quella di copia e la stessa soluzione. Rendi la mutazione un inizializzatore piuttosto che un metodo.

protocol Copyable { 
    init(copy: Self) 
} 

protocol Mutatable : Copyable { 
    init(byMutating: Self) 
} 

class C : Mutatable { 
    var a = 0 

    required init(_ a: Int) { 
    self.a = a 
    } 

    required init(copy: C) { 
    a = copy.a 
    } 

    required convenience init(byMutating: C) { 
    self.init(copy: byMutating) 
    self.a++ 
    } 
} 

// These are purely for convenience 
func copy<T : Copyable>(x: T) -> T { 
    return x.dynamicType(copy: x) 
} 

func mutated<T: Mutatable>(x: T) -> T { 
    return x.dynamicType(byMutating: x) 
} 

Ma per ribadire il punto di Mattt nel articolo collegato, si può avere una sintassi C(copy: x) abbastanza comodamente, e si può avere una sintassi copy(x) abbastanza comodamente, e c'è sempre x.dynamicType(copy: x). Ma non puoi avere una sintassi x.copy() senza qualche fastidioso lavoro. È necessario duplicare func copy() -> Self { return copy(self) } in ogni classe oppure è necessario creare una classe concreta che implementa questo metodo e in definitiva eredita da C. Questo è attualmente un limite di base di Swift. Sono d'accordo con la diagnosi di Mattt sulle possibili soluzioni, e sospetto che in qualche modo un qualche tipo di sistema di tratti, possibilmente sulla falsariga di Scala, verrà aggiunto in futuro.

Vale la pena concentrarsi su commento di Mattt che "tutto questo evidenzia una tensione significativa tra i metodi e le funzioni a Swift." Questo è un altro modo per dire che ci sono tensioni tra il paradigma orientato agli oggetti e il paradigma funzionale, e spostarsi tra di loro può creare qualche disconnessione. Le lingue cercano di risolvere il problema con varie caratteristiche, ma ci sono differenze importanti tra oggetti con messaggi e proprietà, funzioni vs con dati e combinatori e "ottenere il meglio da entrambi i mondi" a volte può creare dei lati negativi.

E 'facile dimenticare, quando si confrontano Swift ad altre lingue, che v'è una grande differenza tra v0.9 e v2.11. Anche molte cose che diamo per scontato nelle nostre lingue preferite non esistevano nella loro v1.


al tuo commento, si può pensare che mutated è di tipo Self. Ma è di tipo C, come indica il completamento automatico. Come in precedenza, C non è lo stesso di Self meno che non si può promettere che non ci sono sottoclassi (C essere o final o struct). I tipi Swift sono risolti in fase di compilazione, non in runtime, a meno che non si usi dynamicType.

Per essere un po 'più specifico, Swift guarda a questa linea:

let mutated = copy(self) 

Essa rileva che copy è generico del tipo di suo parametro, e si deve costruire una versione di copya tempo di compilazione da chiamare. Non esiste il tipo Self. È solo un segnaposto e deve essere risolto in fase di compilazione. Il tipo di self in questo ambito lessicale è C. Quindi costruisce copy<C>. Ma se hai sottoclassificato C, questa potrebbe essere la funzione sbagliata (e in questo caso, sarebbe). Questo è strettamente correlato a: https://stackoverflow.com/a/25549841/97337.

Il fatto che il tipo di completamento automatico dice (C) piuttosto che C è un minore effetto collaterale di come Swift funzioni e tuple di lavoro, e si presenta abbastanza regolarmente, ma ho ancora incontrato un caso in cui è davvero importante. Una funzione Swift come func f(x: Int, y:Int) in realtà non ha due parametri. Ha un parametro di 2 tuple di tipo (Int, Int). Questo fatto è importante per il funzionamento della sintassi currying (per ulteriori informazioni su Swift, vedere Swift Programming Language). Quindi, quando sei specializzato in copy, lo hai specializzato con una tupla di 1 tipo di (C). (O forse, il compilatore sta solo provando a farlo come uno dei vari tentativi, e questo è solo quello su cui si riferisce.) In Swift qualsiasi valore può essere banalmente scambiato per una tupla di 1 dello stesso tipo. Quindi il ritorno di copy è in realtà la 1-tupla di C, scritta (C). Sospetto che il compilatore Swift migliorerà i suoi messaggi nel tempo per rimuovere le parentesi estranee, ma è per questo che a volte vengono visualizzati.

+0

Ok, quindi utilizzare gli inizializzatori per tutto. Fatto. Grazie ancora! – aeubanks

+0

Credo che la mia domanda sia: perché il codice originale non funziona se 'copy (T) -> T' restituisce la classe che lo ha chiamato? Nella funzione 'mutated()' dovrebbe restituire 'Self' giusto? Anche se c'è un modo migliore per farlo, sono ancora curioso di sapere come funziona Swift. – aeubanks

+0

aggiornato con qualche informazione in più –

Problemi correlati