Scala中的3种类型之间的协方差

问题描述

我正在尝试查看是否有找到类型W2方法,该类型是2种类型WE的超级类型。 在我的解决方案中,E代表错误W代表警告。 我要完成的是一种方法or,如果this失败,则运行that并将错误移至警告类型。

这是我在做什么的简化示例。

sealed trait Validator[-I,+E,+W,+A] extends Product with Serializable

这种类型有几种情况,在这里并不是很重要,因此,我将介绍一个示例用法

case class MyObj(coords: GeoCoords)
case class GeoCoords(lat: String,long: String)
        
// Getters
val getLatitude: Validator[GeoCoords,nothing,String] = from[GeoCoords].map(_.lat)
val getLongitude: Validator[GeoCoords,String] = from[GeoCoords].map(_.long)

val parseLatitude: Validator[GeoCoords,Exception,Double] = getLatitude andThen nonEmptyString andThen convertTodouble
val parseLongitude: Validator[GeoCoords,Double] = getLongitude andThen convertTodouble

在这一个不好的例子,但是我想做的是因为parseLatitude错误类型为Exception,所以也许我想给它一个认值,但是仍然可以理解它失败了。我想将ExceptionE错误参数移到W警告参数,如下所示:

val defaultLatitude: Validator[GeoCoords,Double] = success(0)

val finalLatitude: Validator[GeoCoords,Double] = parseLatitude or defaultLatitude

但是同样,如果不是提供认值,而是在or后执行的其他操作也可能失败,因此,情况也应该如此:

val otherAction: Validator[GeoCoords,Throwable,Double] = ???

val finalLatitude: Validator[GeoCoords,Double] = parseLatitude or otherAction

我已经尝试过以多种方式在or上实现Validator,但是每次它给我一个问题时,基本上都是将事情一直投射到Any

def or[I2 <: I,E2 >: E,W2 >: W,B >: A](that: Validator[I2,E2,W2,B]): Validator[I2,B] = Validator.conversion { (i: I2) =>
  val aVal: Validator[Any,E,W,A] = this.run(i)
  val bVal: Validator[Any,B] = that.run(i)

  val a: Vector[W2] = aVal.warnings ++ bVal.warnings
  // PROBLEM HERE
  val b: Vector[Any] = a ++ aVal.errors

  Result(
    aVal.warnings ++ aVal.errors ++ bVal.warnings,bVal.value.toRight(bVal.errors)
  )
}

我必须能够说W2既是W的超类型,也是E的超类型,这样我才能将Vector连接在一起并得到类型{ {1}}结束。

一个超级简化的自包含示例是:

W2

我希望case class Example[+A,+B](a: List[A],b: List[B]) { def or[A2 >: A,B2 >: B](that: Example[A2,B2]): Example[A2,Any] = { Example(that.a,this.a ++ this.b ++ that.a) } } object stackoverflow extends App { val example1 = Example(List(1,2),List(3,4)) val example2 = Example(List(5,6),List(7,8)) val example3 = example1 or example2 } 输出类型是or而不是Example[A2,B2]

解决方法

实际上,您希望将List[A]List[B]串联以产生List[A | B],其中A | Bunion type

How to define "type disjunction" (union types)?

在Scala中,不存在2种联合类型,但是我们可以使用类型类来模拟它们。

因此,对于“超级简化示例”,请尝试类型类LUB(最小上限)

case class Example[A,B](a: List[A],b: List[B]) {
  def or[A2 >: A,B2,AB](that: Example[A2,B2])(
    implicit
    lub: LUB.Aux[A,B,AB],lub1: LUB[AB,A2]
  ): Example[A2,lub1.Out] = {
    Example(that.a,(this.a.map(lub.coerce1) ++ this.b.map(lub.coerce2)).map(lub1.coerce1(_)) ++ that.a.map(lub1.coerce2(_)))
  }
}

val example1: Example[Int,Int] = Example(List(1,2),List(3,4))
val example2: Example[Int,Int] = Example(List(5,6),List(7,8))

val example3 = example1 or example2
//  example3: Example[Int,Any] // doesn't compile
example3: Example[Int,Int] // compiles

trait LUB[A,B] {
  type Out
  def coerce1(a: A): Out
  def coerce2(b: B): Out
}

trait LowPriorityLUB {
  type Aux[A,Out0] = LUB[A,B] { type Out = Out0 }
  def instance[A,Out0](f: (A => Out0,B => Out0)): Aux[A,Out0] = new LUB[A,B] {
    override type Out = Out0
    override def coerce1(a: A): Out0 = f._1(a)
    override def coerce2(b: B): Out0 = f._2(b)
  }

  implicit def bSubtypeA[A,B <: A]: Aux[A,A] = instance(identity,identity)
}

object LUB extends LowPriorityLUB {
  implicit def aSubtypeB[A <: B,B]: Aux[A,B] = instance(identity,identity)
  implicit def default[A,B](implicit ev: A <:!< B,ev1: B <:!< A): Aux[A,Any] = 
    instance(identity,identity)
}

// Testing:
implicitly[LUB.Aux[Int,AnyVal,AnyVal]]
implicitly[LUB.Aux[AnyVal,Int,AnyVal]]
implicitly[LUB.Aux[Int,String,Any]]
implicitly[LUB.Aux[Int,Int]]
//  implicitly[LUB.Aux[Int,Any]] // doesn't compile

<:!<来自这里:

Scala: Enforcing A is not a subtype of B

https://github.com/milessabin/shapeless/blob/master/core/src/main/scala/shapeless/package.scala#L48-L52

如果您希望Example是协变的

case class Example[+A,+B]...

使LUBLUB.Aux互变

trait LUB[-A,-B]...

type Aux[-A,-B,B] { type Out = Out0 }