Scala:函数图上的函数图

发布于 2025-02-03 20:27:28 字数 649 浏览 2 评论 0 原文

给定以下代码

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 不是 map [string,fruat =>字符串] myMap 类似?我猜是因为这是关于功能的,但我希望更好地理解原因。谢谢!

Given the following code

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,
)

Why myMapOfFunctions is not a Map[String, Fruit => String] similarly to myMap? I guess because it is about functions but I'd like better to understand why. Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

冰雪梦之恋 2025-02-10 20:27:28

我只是想理解为什么编译器说该地图的类型是苹果,而不是水果

,好的是,这是“易于” 解释。

坏的是,它可能不太容易理解。

让我们返回几步,让我们通过使用而不是集合来简化代码。

当您执行这样的事情时:

val foo = if (bar) x else y

编译器必须推断 foo 的类型,因为它将首先获得/推断 x y < y < /代码>;让我们称这些 x &amp; y 分别在两者之间计算LUB (最小值上限),导致新的类型 z ,它将是分配给<<的类型代码> foo
这是有道理的,因为 X&lt;:z y&lt;:z ,因此尊重Liskov。

快速注意,如果 x y 是相同的类型 a ,那么lub只是 a br>
另一个快速的,如果 x y 的子类型,那么lub是 y

快速的,如果

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

另一个 具有类型 Apple 和其他橙色,并且两者之间的润滑是 frual
到目前为止,一切都很简单。

现在,让我们稍微调味一点:

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

val optFruit = if (true) optApple else optOrange

这里一个分支是 option [苹果] ,而另一个是 option [橙色] ,我们知道结果将是选项[水果] ,但是为什么?
好吧,因为 option 被定义为在其类型参数上是协变量的 option [frual] 是两个分支机构的超级模型。主要是润滑。

好的,但是功能会发生什么?

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

val fruitFunction = if (true) appleFunction else orangeFunction

在这种情况下,Lub将为(带有橙色的苹果)=&gt;水果 ...但是为什么呢?

好吧,回报很容易,因为功能在回报率上也是协变量的,这意味着lub将再次是 fruit
但是,为什么输入不是那样的?好吧,由于函数在其输入上是违反的,因此对于功能 f 是另一个函数 g 的子类型, f g 的类型代码>必须是 g 的输入的超级类型;即它的顺序相反,这就是为什么它被称为违反。

这样就解释了为什么编译器推断出这种类型。
但是,您可能想知道这种差异业务是什么,为什么重要的以及为什么有一个看起来违反直觉的。但这超出了这个问题的范围&amp;答案。
尽管如此,我可以共享一些可能有用的资源:

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

Okay, the good thing is that this is "easy" to explain.
The bad thing is that it may not be as easy to understand.

Let's take a couple of steps back, and let's simplify the code a little by using an if instead of a collection.

When you do something like this:

val foo = if (bar) x else y

The compiler has to infer the type of foo, for doing that it will first get / infer the types of x and y; let's call those X & Y respectively and then compute the LUB (least upper bound) between both, resulting in a new type Z which will be the type assigned to foo
This makes sense because X <: Z and Y <: Z and thus Liskov is respected.

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

Let's see those applied to simple types:

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

Here, one branch has the type Apple and the other Orange and the LUB between both is Fruit.
Everything has been straightforward until this point.

Now, let's spice the things up a little:

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

val optFruit = if (true) optApple else optOrange

Here one branch is Option[Apple] and the other is Option[Orange], we know that the result will be Option[Fruit], but why?
Well, because Option was defined to be covariant on its type parameter thus Option[Fruit] is a supertype of both branches; and mainly the LUB.

Okay, but what happens with functions?

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

val fruitFunction = if (true) appleFunction else orangeFunction

In this case, the LUB will be (Apple with Orange) => Fruit ... but why?

Well, the return is easy since functions are also covariant on their returns which means the LUB will again be Fruit
But, why is the input not like that? Well, because functions are contravariant on their inputs, thus for a function f to be a subtype of another function g, the type of the input of f must be a super type of the input of g; i.e. it goes in the opposite order which is why it is called contravariance.

So that explains why the compiler inferred that type.
But, you may be wondering what this variance business is, why it matters, and why there is one that seems counterintuitive. But that is outside of the scope of this question & answer.
Nevertheless, I can share a couple of resources that may be useful:

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文