错误:带有默认参数的多态表达式

时间:2012-10-28 14:26:26

标签: scala default-value type-inference

以下是我的错误:

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())      // ???

1 个答案:

答案 0 :(得分:5)

您可以直接在empty方法上指定类型,而不必添加额外的parens / braces和类型注释:

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

至于类型推断失败的原因,请参阅以下问题:

<强>更新

我道歉,我仓促回答了。上述帖子中的问题与此问题无关。 @TravisBrown在上面的评论中提出了非常好的观点。这似乎最初起作用:

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

但是如果你真的试图调用构造函数,它会在use-site:

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

但是,任何具有参数化类型的嵌套都无法在构造函数中的声明站点上键入check:

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 )

如果type参数处于协变位置,推理不会失败:

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

这些工作正常,因为编译器默认选择Nothing作为协变类型。这适用于List,但如果您实际尝试调用它,则上面的第二个示例不起作用。

大多数这种奇怪的原因可能是Scala处理默认参数的方式。编译器会自动向配对对象添加一个额外的方法,然后无论何时省略参数,编译器都会自动向配对对象中的新方法添加方法调用,以生成缺少的参数。看起来像将方法中的默认参数抽象出来会破坏类型推断中的某些东西,这些东西可以用于正常的赋值。

我认为这些调查结果大部分都令人困惑。我从中得到的是,实际测试默认参数以确保它们在您尝试使用它们时不会破坏类型正确性非常重要!