2014-10-08 13 views
5

Si prega di vedere l'esempio contenente il sé di seguito. Il compilatore segnala un errore sull'ultima riga (contrassegnata da COMPILE ERROR) dove sto assegnando un'istanza di SimpleTrain a un tipo di protocollo a cui è conforme (a mio giudizio). Come posso compilarlo? Che cosa sto facendo di sbagliato? O è questo problema del compilatore?Impossibile assegnare un'istanza di classe al suo tipo di protocollo?

protocol Train { 
    typealias CarriageType 

    func addCarriage(carriage: CarriageType) 
    func shortTrain<ShortType: Train where ShortType.CarriageType == CarriageType>() -> ShortType 
} 

class SimpleTrain<T> : Train { 
    typealias CarriageType = T 
    private var carriages: [T] = [T]() 

    func addCarriage(carriage: T) { 
     carriages.append(carriage) 
    } 

    func shortTrain<ShortType: Train where ShortType.CarriageType == CarriageType>() -> ShortType { 
     let short = SimpleTrain<T>() 
     short.addCarriage(carriages[0]) 
     return short //COMPILE ERROR: SimpleTrain<T> is not convertible to 'ShortType' 
    } 
} 

EDIT: Anche quando ho abbattuto esplicitamente s il shortTrain' tipo di ritorno al di sopra (in modo che l'ultima riga di codice frammento di sopra si legge return short as ShortType) come suggested by Antonio c'è ancora errore di compilazione quando si chiama la funzione shortTrain:

let s = SimpleTrain<String>() 
s.addCarriage("Carriage 1") 
s.addCarriage("Carriage 2") 

let a = s.shortTrain() //ERROR: Cannot convert the expression's type '()' to type 'Train' 
let b = s.shortTrain<SimpleTrain<String>>() //ERROR: cannot explicitly specialize a generic function 

risposta

3

In primo luogo, si vuole leggere il canonical thread su questo da devforums. In particolare, vuoi saltare per leggere i commenti di jckarter.

Ora alla domanda modificata:

let a = s.shortTrain() //ERROR: Cannot convert the expression's type '()' to type 'Train' 

Questo è perché non hai dato il compilatore informazioni sufficienti per determinare il tipo di a.Pensare attraverso ciò che vede:

func shortTrain<ShortType: Train where ShortType.CarriageType == CarriageType>() -> ShortType { 
let a = s.shortTrain() 

Il compilatore ha bisogno di capire il tipo di a al momento della compilazione, e non in grado di gestire tipi astratti. Ha bisogno di un tipo completamente specificato per ShortType (tutto inchiodato, tutti i generici specificati, tutti i tipi di alias risolti). Si guarda intorno e vede alcuni vincoli su ShortType, ma non vede nulla che dia effettivamente un tipo. Tutto quello che ha è a, che non gli dà alcun suggerimento.

Quindi, sfortunatamente, questo ti lascia dover dire esplicitamente cosa vuoi che accada.

let a: SimpleTrain<String> = s.shortTrain() 

Questo è probabilmente il contrario di quello che stavi per, ma è tutto quello che si può fare a Swift in questo momento. Il team di Swift ha indicato (molte volte) di essere ben consapevole di questi problemi con i tipi associati (e molti altri punti deboli correlati nel sistema dei tipi). Sono particolarmente consapevoli del sistema di tipo Scala che può gestire queste cose e ha molto in comune con l'attuale sistema di tipo Swift (sebbene nella mia esperienza, ottenere complessi tipi di path dipendenti dipendenti da lavorare in Scala possa anche portare a lacerazioni di capelli).

Detto questo, non è esattamente ovvio dal tuo esempio che cosa hai intenzione di fare con questa funzionalità. Alcuni treni restituiscono un tipo diverso come shortTrain()?

Trovo che questi problemi spesso esplodano nel caso generale, ma tendono ad essere abbastanza risolvibili nei casi specifici per l'app di fronte a voi. È difficile creare tipi veramente arbitrari in Swift in codice che possano risolvere ogni problema, ma spesso si risolve quando ti concentri sui tipi di cui avrai veramente bisogno. Ad esempio, se shortTrain() restituisce Self, questo diventa ovviamente più semplice. Se il chiamante conosce il tipo risultante desiderato, probabilmente un init(shorten:) può gestirlo. Un metodo di protocollo come shortCarriages() -> [CarriageType] può fornire un buon bridge. Rimani flessibile sul tuo design e uno di questi funzionerà quasi sicuramente.

+0

Grazie per questa risposta Rob. Ha risposto a tutte le mie domande (incluse alcune che avrei potuto chiedere altrove in futuro, ad esempio cosa ne pensa il team di Swift). Purtroppo, non posso fornire ulteriori informazioni specifiche. Ho fornito un esempio artificiale per cancellare tutte le informazioni relative al business. Tuttavia, l'esempio fornito è il più vicino possibile e dichiaro che il prodotto è una libreria e difficilmente posso richiedere agli utenti di specificare il tipo esplicitamente per molte chiamate di metodo. Ho trovato un altro modo per aggirare. I protocolli ancora con alias di tipo aperto da utilizzare come tipi sarebbero grandiosi – drasto

4

Un abbattuta esplicito o variabile:

let short = SimpleTrain<T>() as ShortType 

o il valore di ritorno:

return short as ShortType 

risolve il problema.

Aggiornamento

Quando stavo rispondendo alla domanda, mi sono chiesto come il protocollo Train può essere usato come un tipo di ritorno, essendo esso typealiased.

Guardate questo codice:

protocol ProtocolWithNoAlias {   
} 

protocol ProtocolWithAlias { 
    typealias AliasType 
} 

var x: ProtocolWithNoAlias? 
var y: ProtocolWithAlias? 

L'ultima riga viene segnalato come un errore di compilazione:

Protocol 'ProtocolWithAlias' can only be used as a generic constraint because it has Self os associated type requirements 

che significa che non è possibile utilizzare ProtocolWithAlias come un tipo concreto, che a sua volta significa che non è possibile dichiarare variabili aventi il ​​tipo ProtocolWithAlias e, di conseguenza, non ha senso definire la funzione restituendola.

non riesco a trovare alcuna menzione riguardo che nella documentazione ufficiale, ma sono sicuro che letto da qualche parte, solo che non ricordo dove.

Conclusione: interpretare l'errore che stai avendo problemi con:

SimpleTrain<T> is not convertible to 'ShortType' 

come diretta conseguenza del protocollo di essere typealiased.

Si noti che questa è la mia opinione personale, dal momento che non sono in grado in questo momento di dimostrarlo oltre il protocollo di test del snippet di codice con e senza alias.

+0

Grazie per questo Antonio. Mentre è sicuramente una soluzione pratica (e quella che userò se non è stata trovata meglio) credo che un downcast esplicito non dovrebbe essere richiesto in una situazione in cui il compilatore può effettivamente dedurre implicitamente che il tipo assegnato sta adattando la variabile. – drasto

+0

Si prega di leggere la risposta aggiornata - non sono sicuro se ha senso però :) – Antonio

+0

Attraverso il personale, è molto buona opinione e analisi davvero. Inoltre, se continuiamo su queste linee: i protocolli tipografici sono sostitutivi dei protocolli generici (gli ingegneri Apple dichiarano di essere * migliori *).Le tue conclusioni significano che non possono essere usate come tipi (di costanti, variabili, metodi ...). In Java o C# è un equivalente di essere in grado di utilizzare solo interfacce non genetiche come tipi. Se questa analisi è corretta, questa è una grave omissione di IMHO in Swift. Sarei molto interessato a ottenere l'opinione di qualcuno che si possa rendere merito a Swift riguardo al problema. – drasto

0

Avete per utilizzare un protocollo? Non ho una conoscenza molto buona della teoria dei tipi, ma il problema sembra tornare al tema dei "tipi più elevati", in base alla discussione sul thread Apple e dall'alto.

Detto questo, se si può provare a approssimare con una classe base astratta (con l'avvertenza che non è in realtà astratta, e non funziona per le strutture), con la seguente forma:

class AnyTrain<CarriageType> { 

    func addCarriage(carriage: CarriageType) {} 
    func shortTrain() -> AnyTrain<CarriageType> { return AnyTrain<CarriageType>() } 
} 

class SimpleTrain<T> : AnyTrain<T>, Printable { 
    private var carriages: [T] = [T]() 

    override func addCarriage(carriage: T) { 
     carriages.append(carriage) 
    } 

    override func shortTrain() -> AnyTrain<T> { 
     let short = SimpleTrain<T>() 
     short.addCarriage(carriages[0]) 
     return short //COMPILE ERROR: SimpleTrain<T> is not convertible to 'ShortType' 
    } 

    var description:String { 
     return "Train: \(carriages)" 
    } 
} 


let train = SimpleTrain<Int>() 
train.addCarriage(1) 
train.addCarriage(2) 
train.addCarriage(3) 

let shortTrain = train.shortTrain() 
println(shortTrain) 

// obviously you can refer to it as the base 
let anotherTrain:AnyTrain<Int> = SimpleTrain<Int>() 
anotherTrain.addCarriage(3) 
anotherTrain.addCarriage(2) 
anotherTrain.addCarriage(1) 

let anotherShortTrain = anotherTrain.shortTrain() 
println(anotherShortTrain) 

Questo uscite:

Train: [1]

Train: [3]

ho sperimentato questa tecnica per i casi in cui ho gerarchie astratti dove un tipo è in comune, e altri altri differisce ma non influenza le firme funzione. Usando una "classe astratta", posso creare un array di "qualsiasi tipo con questo tipo generico in comune (mentre gli altri possono differire)"

Problemi correlati