2012-10-28 12 views
8

questo mi seguenti bug:errore: l'espressione polimorfa con argomenti di default

trait Foo[A] 
class Bar[A](set: Set[Foo[A]] = Set.empty) 

Questo produce

<console>:8: error: polymorphic expression cannot be instantiated to expected type; 
found : [A]scala.collection.immutable.Set[A] 
required: Set[Foo[?]] 
     class Bar[A](set: Set[Foo[A]] = Set.empty) 
             ^

E 'abbastanza fastidioso che devo ripetere il parametro tipo nei Set.empty. Perché l'inferenza di tipo fallisce con questo argomento predefinito? Le seguenti opere:

class Bar[A](set: Set[Foo[A]] = { Set.empty: Set[Foo[A]] }) 

prega di notare che questo non ha nulla a che fare con Set in particolare:

case class Hallo[A]() 
class Bar[A](hallo: Hallo[A] = Hallo.apply) // nope 

Stranamente non solo questo funziona:

class Bar[A](hallo: Hallo[A] = Hallo.apply[A]) 

... ma anche questo:

class Bar[A](hallo: Hallo[A] = Hallo())  // ??? 
+2

Non una risposta, ma tre cose da notare: potreste voler dare al parametro del tipo qualcosa di diverso da "A" per evitare confusione con il (diverso) 'A' nel' trovato: [A] scala.collection. immutable.Set [A] 'messaggio; il fatto importante sia di 'Set' che di' Hallo' è che sono invarianti (al contrario di 'List'); e la tua ultima riga compilata probabilmente non fa quello che vuoi. –

+1

Mentre 'class Bar [A] (hallo: Hallo [A] = Hallo.apply)' se lo si modifica per usare 'Hallo.apply()' funziona correttamente. Dovresti essere in grado di lasciare il paren spento, quindi dev'essere molto confuso qui. Pensa che stai passando la funzione parzialmente applicata 'Hallo.apply' invece di chiamare' apply' senza argomenti. (Il messaggio di errore dice che ha trovato il tipo '[A]() Hallo [A]'.) – DaoWen

risposta

5

È possibile specificare il tipo direttamente sul metodo empty piuttosto che dover aggiungere il set supplementare di parentesi/bretelle e il tipo di annotazione:

class Bar[A](set: Set[Foo[A]] = Set.empty[Foo[A]]) 

Per quanto riguarda il motivo per cui l'inferenza di tipo non riesce, vedi queste domande :

Aggiornamento:

Mi scuso, la mia risposta frettolosa era fuori strada. Il problema nei post precedenti non è realmente correlato a questo problema. @TravisBrown ha fatto un ottimo punto nel suo commento sopra. Questo sembra funzionare in un primo momento:

class Bar[A](set: Set[A] = Set.empty) 

Ma se in realtà tenta di chiamare il costruttore non riesce a uso loco:

new Bar[Int] 
// <console>:9: error: type mismatch; 
// found : scala.collection.immutable.Set[Nothing] 
// required: Set[Int] 
// Note: Nothing <: Int, but trait Set is invariant in type A. 
// You may wish to investigate a wildcard type such as `_ <: Int`. (SLS 3.2.10) 
// Error occurred in an application involving default arguments. 
//    new Bar[Int] 

Ciò suggerisce che il compilatore non forzare il parametro di default per essere valido per tutti A, solo per alcuni A. Probabilmente fatto questa scelta in modo da poter fare qualcosa di simile:

scala> case class MyClass[T](set: Set[T] = Set(0)) 
defined class MyClass 

scala> MyClass() // defaults to MyClass[Int] 
res0: MyClass[Int] = MyClass(Set(0)) 

scala> MyClass(Set('x)) // but I can still use other types manually 
res1: MyClass[Symbol] = MyClass(Set('x)) 

Tuttavia, qualsiasi tipo di nidificazione con il tipo di parametri non riesce a digitare verificare presso il sito di dichiarazione nel costruttore:

class Bar[A](set: Set[Option[A]] = Set.empty) 
// <console>:7: error: polymorphic expression cannot be instantiated to expected type; 
// found : [A]scala.collection.immutable.Set[A] 
// required: Set[Option[?]] 
//  class Bar[A](set: Set[Option[A]] = Set.empty) 

Il inferenza non sicuro se il parametro tipo è in una posizione covariante:

class Bar[ A ](set: List[Foo[A]] = List.empty) // OK 

class Bar[ A ](set: Map[Int,Foo[A]] = Map.empty) // OK (unless you use it) 

class Bar[ A ](set: Map[Foo[A],Int] = Map.empty) // BAD 
// <console>:8: error: polymorphic expression cannot be instantiated to expected type; 
// found : [A, B]scala.collection.immutable.Map[A,B] 
// required: Map[Foo[?],Int] 
//   class Bar[ A ](set: Map[Foo[A],Int] = Map.empty) // BAD 
//              ^

Questi funzionano perché il compilatore seleziona Nothing come co tipo di variante per impostazione predefinita.Funziona bene per List, ma il secondo esempio sopra non funziona se si tenta effettivamente di chiamarlo.

La causa della maggior parte di questa stranezza è probabilmente il modo in cui Scala gestisce gli argomenti predefiniti. Il compilatore aggiunge automaticamente un metodo extra all'oggetto complementare, e quindi ovunque si ometta un argomento il compilatore aggiunge automaticamente una chiamata al nuovo metodo nell'oggetto complementare per generare invece l'argomento mancante. Sembra che l'astrazione dell'argomento predefinito in un metodo rompa alcune cose nell'inferenza di tipo che funzionerebbe con un normale compito.

Penso che molte di queste scoperte siano piuttosto confuse. Ciò che tolgo a questo è che è importante testare effettivamente i parametri di default per essere certi che non interrompano la correttezza del tipo quando si tenta di usarli!

+0

Sì, sono a conoscenza dei parametri di tipo per 'empty'; Volevo solo dimostrare che il casting risolve completamente il tipo, a differenza del tipo previsto per l'argomento predefinito. Questo non ha senso per me. Inoltre non vedo questo relativo alle domande riguardanti 'toSet' a cui si collega. –

+0

Hai ragione, dopo aver postato la mia risposta mi sono reso conto che non era esattamente lo stesso degli altri due link che ho postato. Lasciatemi fare un po 'più di scavo ... Comunque, dal momento che funziona nel caso in cui usi un 'List' invece di un' Set' nel tuo esempio, penso che sia correlato. – DaoWen

+0

@ 0__ - Ho apportato alcune modifiche sostanziali alla mia risposta, quindi potresti volerci rileggere di nuovo. Non sono ancora sicuro di aver risposto alla tua domanda iniziale, potrei aver solo aggiunto altre domande. – DaoWen

Problemi correlati