2012-10-28 42 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