2012-10-28 44 views
8

這下我的錯誤:錯誤:多態表達默認參數

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

這就產生

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

這是很煩人,我要重複的類型參數Set.empty。這種默認參數爲什麼類型推斷失敗?以下作品:

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

請注意,這有什麼特別的做Set

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

奇怪的是,不僅其工作原理:

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

...但也是這樣的:

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

不是答案,但需要注意三點:爲了避免與'found:[A] scala.collection中的(不同)'A'混淆,您可能想要指定類型參數而不是'A'。 immutable.Set [A]'message;關於'Set'和你的'Hallo'的重要事實是它們是不變的(而不是'List');你最後一行的編譯可能不會做你想要的。 –

+1

雖然'class Bar [A](hallo:Hallo [A] = Hallo.apply)'如果你改變它使用'Hallo.apply()'它可以正常工作。你應該能夠離開這個parens,所以它在這裏一定會變得非常困惑。它認爲你正在傳遞部分應用的函數'Hallo.apply',而不是調用沒有參數的'apply'。 (錯誤消息說它發現類型'[A]()Hallo [A]'。) – DaoWen

回答

5

您可以在empty方法直接指定類型,而不必添加額外的設置括號/括號和類型註釋:

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

至於爲什麼類型推斷失敗,看到這些問題:

更新:

我很抱歉,我匆忙的回答是路要走。上面的帖子中的問題與這個問題並沒有真正的關係。 @TravisBrown在他上面的評論中提到了一個很好的觀點。這似乎在第一工作:

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

但如果你真的嘗試調用它無法在使用現場的構造:

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] 

這表明編譯器不強制默認參數對所有A有效,僅適用於某些A。他們大概做了這個選擇,所以你可以做這樣的事情:

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

然而,任何形式的與參數化類型嵌套無法鍵入構造檢查在聲明的網站:

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) 

的因爲編譯器選擇Nothing作爲共

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

這些工作:推論不會失敗,如果類型參數是在協變位置變體類型默認。這適用於List,但上面的第二個示例在您實際嘗試調用它時不起作用。

大部分這種奇怪的原因可能是Scala處理默認參數的方式。編譯器會自動爲伴隨對象添加一個額外的方法,然後無論您何時離開參數,編譯器都會自動將方法調用添加到伴隨對象中的新方法以生成缺少的參數。它看起來像將缺省參數抽象爲一個方法會破壞類型推斷中的一些東西,這些東西可以用於正常的賦值。

我認爲這些發現大部分都相當混亂。我從中獲得的是,實際測試默認參數非常重要,以確保在嘗試使用它們時不會破壞類型正確性!

+0

是的,我知道'empty'的類型參數;我只是想表明該演員完全解析了該類型,與默認參數的預期類型不同。這對我沒有意義。此外,我沒有看到這與您鏈接到的'toSet'有關的問題。 –

+0

你是對的,在我發佈我的答案後,我意識到這與我發佈的其他兩個鏈接並不完全相同。讓我做更多的挖掘......但是,如果你在你的例子中使用'List'而不是'Set',它會起作用,我認爲它是相關的。 – DaoWen

+0

@ 0__ - 我對我的回答做了一些非常實質性的修改,所以您可能需要再次閱讀。我仍然不確定我是否回答了您的最初問題 - 我可能只會添加更多問題。 – DaoWen