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

0__*_*0__ 8 scala type-inference default-value

以下是我的错误:

trait Foo[A]
class Bar[A](set: Set[Foo[A]] = Set.empty)
Run Code Online (Sandbox Code Playgroud)

这产生了

<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)
                                           ^
Run Code Online (Sandbox Code Playgroud)

我必须重复类型参数,这非常烦人Set.empty.为什么类型推断失败了这个默认参数?以下作品:

class Bar[A](set: Set[Foo[A]] = { Set.empty: Set[Foo[A]] })
Run Code Online (Sandbox Code Playgroud)

请注意,这与此无关Set:

case class Hallo[A]()
class Bar[A](hallo: Hallo[A] = Hallo.apply)  // nope
Run Code Online (Sandbox Code Playgroud)

奇怪的是,这不仅有效:

class Bar[A](hallo: Hallo[A] = Hallo.apply[A])
Run Code Online (Sandbox Code Playgroud)

......还有这个:

class Bar[A](hallo: Hallo[A] = Hallo())      // ???
Run Code Online (Sandbox Code Playgroud)

Dao*_*Wen 5

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

class Bar[A]( set: Set[Foo[A]] = Set.empty[Foo[A]] )
Run Code Online (Sandbox Code Playgroud)

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

更新:

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

class Bar[A]( set: Set[A] = Set.empty )
Run Code Online (Sandbox Code Playgroud)

但是,如果您实际尝试调用构造函数,则它在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]
Run Code Online (Sandbox Code Playgroud)

这表明编译器不会强制默认参数对所有人都有效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))
Run Code Online (Sandbox Code Playgroud)

但是,任何与参数化类型的嵌套都无法在构造函数中的声明站点上键入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 )
Run Code Online (Sandbox Code Playgroud)

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

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
//                                                       ^
Run Code Online (Sandbox Code Playgroud)

这些工作正常,因为编译器Nothing默认选择协变类型.这样可以正常工作List,但如果你真的尝试调用它,上面的第二个例子就不起作用了.

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

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