2012-10-28 13 views
8

ta następujące błędy mnie:błąd: polimorficzny wyrażenie z domyślnych argumentów

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

Daje

<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) 
             ^

Jest to dość irytujące, że muszę powtórzyć parametr typu w Set.empty. Dlaczego wnioskowanie typu nie działa z tym domyślnym argumentem? Następujące prace:

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

Należy pamiętać, że to nie ma nic wspólnego z Set w szczególności:

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

dziwo nie tylko to działa:

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

... ale także:

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

Nie jest to odpowiedź, ale trzy rzeczy do zapamiętania: możesz chcieć nazwać parametr typu coś innego niż 'A', aby uniknąć pomyłki z (innym)' A' w 'found: [A] scala.collection. immutable.Set [A] 'message; ważnym faktem dotyczącym zarówno 'Set', jak i' Hallo' jest to, że są niezmienne (w przeciwieństwie do 'List'); a twoja ostatnia kompilacja linii prawdopodobnie nie robi tego, co chcesz. –

+1

Podczas gdy 'class Bar [A] (hallo: Hallo [A] = Hallo.apply)' jeśli zmienisz go, aby używał 'Hallo.apply()', to działa dobrze. Powinnaś móc opuścić pareny, więc tutaj musi być naprawdę niezrozumiała. Uważa, że ​​przekazujesz częściowo zastosowaną funkcję 'Hallo.apply' zamiast wywoływać' apply' bez argumentów. (Komunikat o błędzie mówi, że znalazł typ '[A]() Hallo [A]'.) – DaoWen

Odpowiedz

5

można określić typ bezpośrednio na metodzie empty zamiast dodać dodatkowy zestaw parens/szelki i adnotacji typu:

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

Jak, dlaczego typ wnioskowanie nie patrz na te pytania :

Aktualizacja:

przepraszam, mój pochopna odpowiedź była daleko. Problem w powyższych postach nie jest tak naprawdę związany z tym problemem. @TravisBrown przedstawił bardzo dobry punkt w swoim komentarzu powyżej. To wydaje się działać na początku:

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

Ale jeśli rzeczywiście starają się wywołać konstruktor nie jest on w miejscu użytkowania:

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] 

Sugeruje to, że kompilator nie wymusza parametr domyślny obowiązuje dla wszystkich A, tylko dla niektórych A. Prawdopodobnie popełnił takiego wyboru, więc można zrobić coś takiego:

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)) 

Jednakże każdy rodzaj zagnieżdżania z parametryzowanego typu niepomyślnym typ kontroli w miejscu deklaracji w konstruktorze:

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) 

The wnioskowanie nie powiedzie się, jeśli parametr typ jest w kowariantna stanowisko:

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 
//              ^

są to działa, ponieważ kompilator wybiera Nothing jako współ typ wariantu domyślnie.To działa dobrze dla List, ale drugi przykład powyżej nie działa, jeśli faktycznie próbujesz go wywołać.

Przyczyną większości tego dziwactwa jest prawdopodobnie sposób, w jaki Scala obsługuje domyślne argumenty. Kompilator automatycznie dodaje dodatkową metodę do obiektu towarzyszącego, a następnie, gdy pominięto argument, kompilator automatycznie dodaje wywołanie metody do nowej metody w obiekcie towarzyszącym, aby zamiast tego wygenerować brakujący argument. Wygląda na to, że wyodrębnienie domyślnego argumentu w metodzie powoduje zerwanie niektórych rzeczy w procesie wnioskowania, które działałyby przy normalnym zadaniu.

Myślę, że większość z tych ustaleń jest dość myląca. Rozumiem, że ważne jest, aby właściwie przetestować parametry domyślne, aby upewnić się, że nie łamią poprawności typu, gdy próbujesz ich użyć!

+0

Tak, znam parametry typu dla 'empty'; Chciałem tylko pokazać, że rzutowanie w pełni rozwiązuje typ, w przeciwieństwie do oczekiwanego typu dla domyślnego argumentu. To nie ma dla mnie sensu. Również nie widzę, że jest to związane z pytaniami dotyczącymi 'toSet', z którym łączysz. –

+0

Masz rację, po tym, jak napisałem swoją odpowiedź, zdałem sobie sprawę, że to nie jest tak samo jak pozostałe dwa linki, które zamieściłem. Pozwól mi zrobić trochę więcej kopania ... Jednak, ponieważ działa, jeśli użyjesz 'List' zamiast' Set' w twoim przykładzie, myślę, że to jest powiązane. – DaoWen

+0

@ 0__ - Wprowadziłem kilka istotnych poprawek do mojej odpowiedzi, więc możesz chcieć przeczytać to jeszcze raz. Nadal nie jestem pewien, czy odpowiedziałem na twoje pierwsze pytanie - mogłem tylko dodać więcej pytań. – DaoWen

Powiązane problemy