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 copy
a 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.
Ok, quindi utilizzare gli inizializzatori per tutto. Fatto. Grazie ancora! – aeubanks
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
aggiornato con qualche informazione in più –