我正在尝试使用Scala,并且稍微混淆了类型推断问题。
鉴于下面的定义,它成功编译:
case class SequentialHistory[+E](events: Seq[E])
trait Event
case class SampleEvent(value: String) extends Event
def addEvent[E, E2 >: E](event: E)(history:SequentialHistory[E2]):
SequentialHistory[E2] = history.copy(events = event +: history.events)
我想了解为什么类型推断不能正常工作并编译:
val hist = SequentialHistory(Seq.empty[Event])
//hist: SequentialHistory[Event] = SequentialHistory(List())
val histWithEvent = addEvent(SampleEvent("Does not compile"))(hist)
//error
导致编译器错误:
Error:(21, 62) type mismatch;
found : SequentialHistory[Event]
required: SequentialHistory[SampleEvent]
addEvent(SampleEvent("Does not compile"))(hist)
^
但是,如果我在addEvent
方法定义中交换参数列表:
def addEvent2[E, E2 >: E](history: SequentialHistory[E2])(event: E):
SequentialHistory[E2] = history.copy(events = event +: history.events)
将解决问题,正确推断类型和下面的代码片段编译:
val hist2 = SequentialHistory(Seq.empty[Event])
//hist2: SequentialHistory[Event] = SequentialHistory(List())
val histWithEvent2 = addEvent2(hist)(SampleEvent("Compiles"))
//histWithEvent1: SequentialHistory[Event] =
// SequentialHistory(List(SampleEvent1(Compiles)))
为什么Scala编译器无法在第一版addEvent
中正确推断类型?
答案 0 :(得分:1)
我无法通过引用Scala规范中的编译器代码或引用来解释它,但这是我的推理。
当你的函数有多个参数列表的时候,它就是" curried",所以它是第一个返回另一个函数的参数的函数,等等。所以如果你尝试应用它只需一个参数,所有类型都将被推断出来:
scala> val partial = addEvent(SampleEvent(""))(_)
partial: SequentialHistory[SampleEvent] => SequentialHistory[SampleEvent] = <function1>
现在你不能用更宽的类型传递第二个参数。这里发生的是eta expansion。但另一种方式是它不会起作用:
scala> val partial = addEvent2(hist)(_)
<console>:14: error: missing parameter type for expanded function ((x$1) => addEvent2(hist)(x$1))
val partial = addEvent2(hist)(_)
此处E2
由hist
参数修复,但编译器无法推断E
。但是如果你提供两个参数,它会看到它们的类型符合限制。我认为在这个意义上,第二个定义与定义它def addEvent2[E, E2 >: E](event: E, history: SequentialHistory[E2])
。