Scala:函数图上的函数图
给定以下代码
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
类似?我猜是因为这是关于功能的,但我希望更好地理解原因。谢谢!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
,好的是,这是“易于” 解释。
坏的是,它可能不太容易理解。
让我们返回几步,让我们通过使用而不是集合来简化代码。
当您执行这样的事情时:
编译器必须推断
foo
的类型,因为它将首先获得/推断x
和y <
y <
/代码>;让我们称这些
x
&amp;y
分别在两者之间计算LUB (最小值上限),导致新的类型z
,它将是分配给<<的类型代码> foo这是有道理的,因为
X&lt;:z
和y&lt;:z
,因此尊重Liskov。快速的,如果
另一个 具有类型
Apple
和其他橙色
,并且两者之间的润滑是frual
。到目前为止,一切都很简单。
现在,让我们稍微调味一点:
这里一个分支是
option [苹果]
,而另一个是option [橙色]
,我们知道结果将是选项[水果]
,但是为什么?好吧,因为
option
被定义为在其类型参数上是协变量的option [frual]
是两个分支机构的超级模型。主要是润滑。好的,但是功能会发生什么?
在这种情况下,Lub将为
(带有橙色的苹果)=&gt;水果
...但是为什么呢?好吧,回报很容易,因为功能在回报率上也是协变量的,这意味着lub将再次是
fruit
但是,为什么输入不是那样的?好吧,由于函数在其输入上是违反的,因此对于功能
f
是另一个函数g
的子类型,f
g 的类型代码>必须是g
的输入的超级类型;即它的顺序相反,这就是为什么它被称为违反。这样就解释了为什么编译器推断出这种类型。
但是,您可能想知道这种差异业务是什么,为什么重要的以及为什么有一个看起来违反直觉的。但这超出了这个问题的范围&amp;答案。
尽管如此,我可以共享一些可能有用的资源:
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:
The compiler has to infer the type of
foo
, for doing that it will first get / infer the types ofx
andy
; let's call thoseX
&Y
respectively and then compute the LUB (least upper bound) between both, resulting in a new typeZ
which will be the type assigned tofoo
This makes sense because
X <: Z
andY <: Z
and thus Liskov is respected.Let's see those applied to simple types:
Here, one branch has the type
Apple
and the otherOrange
and the LUB between both isFruit
.Everything has been straightforward until this point.
Now, let's spice the things up a little:
Here one branch is
Option[Apple]
and the other isOption[Orange]
, we know that the result will beOption[Fruit]
, but why?Well, because
Option
was defined to be covariant on its type parameter thusOption[Fruit]
is a supertype of both branches; and mainly the LUB.Okay, but what happens with functions?
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 functiong
, the type of the input off
must be a super type of the input ofg
; 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: