Scala:求和类型的函数映射

Scala: Map of functions on sum types

给定以下代码

sealed trait Fruit

case class Apple(color: String) extends Fruit
case class Orange(color: String) extends Fruit

def getAppleColor(apple: Apple) = apple.color
def getOrangeColor(orange: Orange) = orange.color

val myMap: Map[String, Fruit] = Map(
"myApple" -> Apple("red"),
"myOrange" -> Orange("orange"),
)

val myMapOfFunctions: Map[String, Apple with Orange => String]  = Map(
"myAppleColorFun" -> getAppleColor,
"myOrangeColorFun" -> getOrangeColor,
)

为什么 myMapOfFunctions 不是类似于 myMapMap[String, Fruit => String]?我猜是因为它是关于功能的,但我想更好地理解为什么。谢谢!

I am just trying to understand why the compiler says that the type of the map is Apple with Orange and not Fruit

好的,好处是这“容易”解释。
不好的是可能没那么容易理解。

让我们退后几步,通过使用 if 而不是集合来稍微简化代码。

当你做这样的事情时:

val foo = if (bar) x else y

编译器必须推断 foo 的类型,为此它首先会获取/推断 xy 的类型;让我们分别调用它们 XY,然后计算两者之间的 LUB (最小上限),得到一个新类型 Z,它将是分配给 foo
的类型 这是有道理的,因为 X <: ZY <: Z 因此 Liskov 受到尊重。

Quick note, if X and Y are the same types A then the LUB is just A
Another quick one, if X is a subtype of Y then the LUB is simply Y

让我们看看那些应用于简单类型的:

val fruit = if (true) Apple(color = "red") else Orange(color = "green")

此处,一个分支的类型为 Apple,另一个分支的类型为 Orange,两者之间的 LUB 为 Fruit.
到目前为止,一切都很简单。

现在,让我们来点趣味剂:

val optApple: Option[Apple] = Apple(color = "red")
val optOrange: Option[Orange] = Orange(color = "green")

val optFruit = if (true) optApple else optOrange

这里一个分支是Option[Apple],另一个分支是Option[Orange],我们知道结果会是Option[Fruit],但是为什么呢?
好吧,因为 Option 被定义为在其类型参数上是协变的,因此 Option[Fruit] 是两个分支的超类型;主要是 LUB。

好的,但是函数会怎样?

// Implementations do not matter.
val appleFunction : Apple => Apple = ???
val orangeFunction: Orange => Orange = ???

val fruitFunction = if (true) appleFunction else orangeFunction

在这种情况下,LUB 将是 (Apple with Orange) => Fruit ... 但是为什么呢?

嗯,return 很简单,因为函数在它们的 return 上也是协变的,这意味着 LUB 将再次是 Fruit
但是,为什么输入不是那样的呢?好吧,因为函数在它们的输入上是逆变的,因此函数 f 是另一个函数 g 的子类型,f 的输入类型必须是g 的输入;即它以相反的顺序进行,这就是为什么它被称为逆变。

这就解释了编译器推断该类型的原因。
但是,您可能想知道这种差异业务是什么,为什么它很重要,以及为什么有一个看起来违反直觉的业务。但这超出了本问答的范围。
不过,我可以分享一些可能有用的资源: