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(即

我理解scala中的协变和逆变。协方差在现实世界中有很多应用,但我想不出任何用于逆变的应用,除了函数的相同的老例子


有人能解释一下真实世界中
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)