2016-06-20 18 views
6

Nel seguente esempio, f3 può prendere una Iterable [Array [Int]]L'assegnazione locale ha effetto sul tipo?

def f3(o:Iterable[Iterable[Any]]):Unit = {} 

    f3(Iterable(Array(123))) // OK. Takes Iterable[Array[Int]] 

ma se assegno l'Iterable [Array [Int]] a una variabile locale, non può:

val v3 = Iterable(Array(123)) 
    f3(v3) // Fails to take Takes Iterable[Array[Int]] 

con l'errore:

Error:(918, 10) type mismatch; 
    found : Iterable[Array[Int]] 
    required: Iterable[Iterable[Any]] 
    f3(x) 

Che il fondente? Perché funziona il primo esempio, ma non i secondi. Sembra avere qualcosa a che fare con i generici nidificati:

def f(o:Iterable[Any]):Unit = {} 
    f(Array(123)) 
    val v1 = Array(123) 
    f(v1) // OK 

    def f2(o:Iterable[Any]):Unit = {} 
    f2(Iterable(Array(123))) 
    val v2 = Array(123) 
    f(v2) // OK 

Con scala.2.11

+0

Perché "Iterable [Any]'? Questo è proprio come non usare generici/tipo affatto. – cchantep

+0

Qualsiasi è l'interfaccia comune dell'oggetto che ho bisogno nelle mie raccolte. In alcuni contesti, Scala diventa pissy se non si specifica un tipo di parametro e invece Inferiore, in particolare quando si restituiscono i generici. – user48956

+0

Principalmente se hai bisogno di 'Any', c'è un problema di progettazione – cchantep

risposta

6

Prima di tutto, è importante che non si estende ArrayIterable (perché è un tipo Java). Invece c'è una conversione implicita da Array[A] a Iterable[A], quindi i tipi previsti sono importanti.

Nel primo caso: Iterable(Array(123)) è un argomento per f3 e pertanto è contrassegnato con il tipo previsto Iterable[Iterable[Any]]. Pertanto, Array(123) è digitato con il tipo previsto Iterable[Any]. Bene, il suo tipo effettivo è Array[Int] e il compilatore inserisce la conversione (perché Iterable[Int] è conforme a Iterable[Any]). Quindi questo è in realtà Iterable(array2iterable(Array(123)) (non ricordo il nome esatto).

Nel secondo caso f3 ha il tipo Iterable[Array[Int]]: non c'è nulla per attivare la conversione implicita nella riga val f3 = ..., giusto? E non vi è alcuna conversione implicita da Iterable[Array[Int]] a Iterable[Iterable[Int]] (o, più in generale da Iterable[A] a Iterable[B] quando c'è una conversione implicita da A a B), quindi la riga successiva non riesce a compilare. Puoi scrivere tu stesso questa conversione, ma non sarebbe di aiuto ad es. convertire Array[Array[Int]] in Iterable[Iterable[Int]].

E, naturalmente, se si utilizza Iterable[Any], non c'è ancora nulla per attivare la conversione implicita!

+0

Questo non spiega perché la conversione esplicita avviene nel primo caso, ma non secondo:" non c'è conversione implicita da ... a ... "può essere corretto, ma allora perché funziona il primo caso? – Dima

+0

@Dima Il primo caso funziona perché esiste una conversione implicita da "Array [Int]" a "Iterable [Int]". Quindi, dal momento che utilizziamo un array in cui è disponibile un iterabile, è possibile utilizzare la conversione. Nel secondo caso la riga 'val v3 = Iterable (Array (123))' non si aspetta 'Iterable [Int]' da nessuna parte, quindi non è necessaria alcuna conversione implicita per far funzionare quella linea. Abbiamo quindi un problema nella riga successiva, ma a quel punto è troppo tardi per aggiungere una conversione implicita su 'Array (123)' (perché è già stato eseguito sulla riga precedente). Nel migliore dei casi potremmo avere uno attorno a 'v3', ma come dice Alexey, non esiste uno adatto. – sepp2k

+0

@Dima Er ... lo fa? "So Array (123) è tipografato con il tipo previsto Iterable [Any]. Beh, il suo tipo effettivo è Array [Int] e il compilatore inserisce la conversione (perché Iterable [Int] è conforme a Iterable [Any])." –

4

Questo ha a che fare con il modo in cui l'inferenza di tipo/l'unificazione funziona in Scala.

Quando si definisce una variabile e lasciare fuori il tipo, Scala di applicare il tipo più specifica possibile:

scala> val v1 = Iterable(Array(123)) 
v1: Iterable[Array[Int]] = List(Array(123)) 

Tuttavia, quando si specifica il tipo previsto (ad esempio passando il valore di una funzione con una definita parametro del tipo) Scala unifica il parametro data con il tipo atteso (se possibile):

scala> val v2 : Iterable[Iterable[Any]] = Iterable(Array(123)) 
v2: Iterable[Iterable[Any]] = List(WrappedArray(123)) 

Dal Int è un sottotipo di Any, si verifica l'unificazione e il codice viene eseguito bene.

Se si desidera che la funzione accetti tutto ciò che è un sottotipo di Any (senza l'aiuto di unificazione di Scala), è necessario definire questo comportamento in modo esplicito.

Edit:

Mentre quello che sto dicendo è parzialmente vero, vedere @ risposta di AlexyRomanov per una valutazione più corretta. Sembra che la "unificazione" tra Array e Iterable sia in realtà una conversione implicita chiamata quando si passa Iterable(Array(123)) come parametro (vedere l'effetto di ciò nella mia dichiarazione di v2).

Supponiamo di avere un po 'di codice in cui il compilatore si aspetta il tipo B ma trova invece il tipo A. Prima di generare un errore, il compilatore controlla una raccolta di funzioni di conversione implicite per una con il tipo A => B. Se il compilatore trova una conversione soddisfacente, la conversione viene applicata automaticamente (e in silenzio).

Il motivo f3 non ama v1 è perché è troppo tardi per chiamare una conversione implicita sul interna Array[Int] e non esiste alcuna conversione implicita esistente per Iterable[Array[Int]] => Iterable[Iterable[Int]], anche se sarebbe banale da implementare, come mostro qui di seguito:

scala> implicit def ItAr2ItIt[T](ItAr: Iterable[Array[T]]): Iterable[Iterable[T]] = ItAr.map(_.toIterable) 
ItAr2ItIt: [T](ItAr: Iterable[Array[T]])Iterable[Iterable[T]] 

scala> def f3(o:Iterable[Iterable[Any]]):Unit = println("I like what I see!") 
f3: (o: Iterable[Iterable[Any]])Unit 

scala> val v3 = Iterable(Array(123)) 
v3: Iterable[Array[Int]] = List(Array(123)) 

scala> f3(v3) 
I like what I see!