Scala逆变-真实生活示例
我理解scala中的协变和逆变。协方差在现实世界中有很多应用,但我想不出任何用于逆变的应用,除了函数的相同的老例子Scala逆变-真实生活示例,scala,computer-science,Scala,Computer Science,我理解scala中的协变和逆变。协方差在现实世界中有很多应用,但我想不出任何用于逆变的应用,除了函数的相同的老例子 有人能解释一下真实世界中contrance用法的例子吗 这个例子来自我上一个项目。假设您有一个类型类PrettyPrinter[a],它为a类型的漂亮打印对象提供逻辑。现在如果B>:A(即B是A的超类)并且您知道如何漂亮地打印B(即有一个PrettyPrinter[B]的实例可用),那么您可以使用相同的逻辑漂亮地打印A。换句话说,B>:A意味着PrettyPrinter[B]A(即
有人能解释一下真实世界中
contrance
用法的例子吗 这个例子来自我上一个项目。假设您有一个类型类PrettyPrinter[a]
,它为a
类型的漂亮打印对象提供逻辑。现在如果B>:A
(即B
是A
的超类)并且您知道如何漂亮地打印B
(即有一个PrettyPrinter[B]
的实例可用),那么您可以使用相同的逻辑漂亮地打印A
。换句话说,B>:A
意味着PrettyPrinter[B]A
(即任意关系)。如果您可以为数据类型定义一个操作contramap
,该操作可以将B=>a
类型的函数提升到F[a=>B]
,则称其为逆变函子
需要满足以下法律:
x.contracmap(身份)
=x
x.contracmap(f).contracmap(g)
=x.contracmap(f.g)
Show
,Equal
等)都是逆变函子。此属性允许我们执行有用的操作,如下图所示:
假设您有一个类候选者定义为:
case class Candidate(name: String, age: Int)
您需要一个Order[Candidate]
来按年龄对候选人进行排序。现在您知道有一个可用的Order[Int]
实例。您可以使用contramap
操作从中获取Order[Candidate]
实例:
val byAgeOrder: Order[Candidate] =
implicitly[Order[Int]] contramap ((_: Candidate).age)
在我看来,Function
之后最简单的两个例子是排序和相等。然而,第一个在Scala的标准库中并不是反向变体,第二个甚至不存在于其中。所以,我将使用Scalaz等价物:和
接下来,我需要一些类层次结构,最好是熟悉的层次结构,当然,上面的两个概念都必须对它有意义。如果Scala有一个所有数字类型的数字
超类,那就太完美了。不幸的是,它没有这样的东西
所以我要试着用集合来做例子。为了简单起见,让我们考虑<代码> SEQ[INT] < /COD>和<代码>列表[INT] < /代码>。应该清楚的是,List[Int]
是Seq[Int]
的一个子类型,即,List[Int]是一个基于真实世界事件驱动软件系统的示例。这样的系统基于广泛的事件类别,例如与系统功能相关的事件(系统事件)、由用户操作生成的事件(用户事件)等等
可能的事件层次结构:
trait Event
trait UserEvent extends Event
trait SystemEvent extends Event
trait ApplicationEvent extends SystemEvent
trait ErrorEvent extends ApplicationEvent
现在,在事件驱动系统上工作的程序员需要找到一种方法来注册/处理系统中生成的事件。它们将创建一个特性,Sink
,用于标记在触发事件时需要通知的组件
trait Sink[-In] {
def notify(o: In)
}
由于使用-
符号标记类型参数,接收器类型变为逆变型
通知相关方事件发生的一种可能方法是编写一个方法并将其传递给相应的事件。假设此方法将执行一些处理,然后它将负责通知事件接收器:
def appEventFired(e: ApplicationEvent, s: Sink[ApplicationEvent]): Unit = {
// do some processing related to the event
// notify the event sink
s.notify(e)
}
def errorEventFired(e: ErrorEvent, s: Sink[ErrorEvent]): Unit = {
// do some processing related to the event
// notify the event sink
s.notify(e)
}
几个假设的接收器实现
trait SystemEventSink extends Sink[SystemEvent]
val ses = new SystemEventSink {
override def notify(o: SystemEvent): Unit = ???
}
trait GenericEventSink extends Sink[Event]
val ges = new GenericEventSink {
override def notify(o: Event): Unit = ???
}
编译器接受以下方法调用:
appEventFired(new ApplicationEvent {}, ses)
errorEventFired(new ErrorEvent {}, ges)
appEventFired(new ApplicationEvent {}, ges)
查看一系列调用,您会注意到,使用Sink[SystemEvent]
甚至使用Sink[Event]
调用一个预期为Sink[ApplicationEvent]
的方法是可能的。另外,您可以使用Sink[Event]
调用预期为Sink[ErrorEvent]
的方法
通过使用逆变约束替换不变性,Sink[SystemEvent]
将成为Sink[ApplicationEvent]
的子类型。因此,反向变异也可以被认为是一种“扩大”关系,因为类型从更具体的“扩大”到更一般的
结论
本例已在一系列关于在上发现的差异的文章中描述
最后,我认为这也有助于理解其背后的理论…简短的回答可能会帮助像我这样极度困惑的人,他们不想阅读这些冗长的例子:
假设您有两个类Animal
,和Cat
,它们扩展了Animal
。现在,假设您有一个类型Printer[Cat]
,其中包含打印Cat
s的功能。你有这样一种方法:
scala> smaller(List(1), List(1, 2, 3))
res0: List[Int] = List(1)
def打印(p:Printer[Cat],Cat:Cat)=p.print(Cat)
但问题是,既然Cat
是Animal
,Printer[Animal]
也应该能够打印Cat
s,对吗
好的,如果Printer[T]
被定义为Printer[-T]
,即逆变,那么我们可以将Printer[Animal]
传递给上面的print
函数,并使用其功能来打印猫
这就是矛盾存在的原因。另一个例子,例如来自C#
,是类IComparer
,它也是逆变的。为什么?因为我们应该能够使用Animal
比较器来比较Cat
s。参见Daniel Spiewak对……的回答,因为现实世界中没有人使用函数?=)有scala.Equiv,但它也不是逆变的。在您的“更小”示例中,我是否可以这样做:defmiler(a:List[Int],b:List[Int])(隐式Order:Order[Seq[Int]]=if(Order.Order(a,b)==LT)a else b
?而不是Order[List[Int]
trait Event
trait UserEvent extends Event
trait SystemEvent extends Event
trait ApplicationEvent extends SystemEvent
trait ErrorEvent extends ApplicationEvent
trait Sink[-In] {
def notify(o: In)
}
def appEventFired(e: ApplicationEvent, s: Sink[ApplicationEvent]): Unit = {
// do some processing related to the event
// notify the event sink
s.notify(e)
}
def errorEventFired(e: ErrorEvent, s: Sink[ErrorEvent]): Unit = {
// do some processing related to the event
// notify the event sink
s.notify(e)
}
trait SystemEventSink extends Sink[SystemEvent]
val ses = new SystemEventSink {
override def notify(o: SystemEvent): Unit = ???
}
trait GenericEventSink extends Sink[Event]
val ges = new GenericEventSink {
override def notify(o: Event): Unit = ???
}
appEventFired(new ApplicationEvent {}, ses)
errorEventFired(new ErrorEvent {}, ges)
appEventFired(new ApplicationEvent {}, ges)