Scala 中 3 种类型之间的协方差
Covariance between 3 types in Scala
我想看看是否有办法找到类型 W2
,它是 2 种类型 W
和 E
的超类型。
在我的解决方案中,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, Nothing, String] = from[GeoCoords].map(_.lat)
val getLongitude: Validator[GeoCoords, Nothing, Nothing, String] = from[GeoCoords].map(_.long)
val parseLatitude: Validator[GeoCoords, Exception, Nothing, Double] = getLatitude andThen nonEmptyString andThen convertToDouble
val parseLongitude: Validator[GeoCoords, Exception, Nothing, Double] = getLongitude andThen convertToDouble
现在这是一个不好的例子,但我想要做的是因为 parseLatitude
的错误类型是 Exception
,也许我想给出一个默认值,但仍然明白失败了。我想将 Exception
从 E
错误参数移动到 W
警告参数,如下所示:
val defaultLatitude: Validator[GeoCoords, Nothing, Nothing, Double] = success(0)
val finalLatitude: Validator[GeoCoords, Nothing, Exception, Double] = parseLatitude or defaultLatitude
但是,如果不提供默认值,我在 or
之后采取的其他操作也可能会失败,因此情况也应该如此:
val otherAction: Validator[GeoCoords, Throwable, Nothing, Double] = ???
val finalLatitude: Validator[GeoCoords, Throwable, Exception, Double] = parseLatitude or otherAction
我已经尝试在 Validator
类型上以多种方式实现 or
,但每次它都会给我一个问题,基本上一直转换到 Any
。
def or[I2 <: I, E2 >: E, W2 >: W, B >: A](that: Validator[I2, E2, W2, B]): Validator[I2, E2, W2, B] = Validator.conversion { (i: I2) =>
val aVal: Validator[Any, E, W, A] = this.run(i)
val bVal: Validator[Any, E2, W2, 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
连接在一起并得到在末尾键入 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 Whosebug 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]
而不是 Example[A2, Any]
实际上你想要 List[A]
和 List[B]
的串联产生 List[A | B]
,其中 A | B
是 union type.
How to define "type disjunction" (union types)?
在 Scala 中没有联合类型 2 但我们可以用类型 classes 来模拟它们。
所以关于“超级简化的例子”尝试一个类型class 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, B, Out0] = LUB[A, B] { type Out = Out0 }
def instance[A, B, Out0](f: (A => Out0, B => Out0)): Aux[A, B, 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, B, A] = instance(identity, identity)
}
object LUB extends LowPriorityLUB {
implicit def aSubtypeB[A <: B, B]: Aux[A, B, B] = instance(identity, identity)
implicit def default[A, B](implicit ev: A <:!< B, ev1: B <:!< A): Aux[A, B, 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, Int]]
// implicitly[LUB.Aux[Int, AnyVal, Any]] // doesn't compile
<:!<
来自这里:
Scala: Enforcing A is not a subtype of B
如果你希望Example
是协变的
case class Example[+A, +B]...
使LUB
和LUB.Aux
逆变
trait LUB[-A, -B]...
type Aux[-A, -B, Out0] = LUB[A, B] { type Out = Out0 }
我想看看是否有办法找到类型 W2
,它是 2 种类型 W
和 E
的超类型。
在我的解决方案中,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, Nothing, String] = from[GeoCoords].map(_.lat)
val getLongitude: Validator[GeoCoords, Nothing, Nothing, String] = from[GeoCoords].map(_.long)
val parseLatitude: Validator[GeoCoords, Exception, Nothing, Double] = getLatitude andThen nonEmptyString andThen convertToDouble
val parseLongitude: Validator[GeoCoords, Exception, Nothing, Double] = getLongitude andThen convertToDouble
现在这是一个不好的例子,但我想要做的是因为 parseLatitude
的错误类型是 Exception
,也许我想给出一个默认值,但仍然明白失败了。我想将 Exception
从 E
错误参数移动到 W
警告参数,如下所示:
val defaultLatitude: Validator[GeoCoords, Nothing, Nothing, Double] = success(0)
val finalLatitude: Validator[GeoCoords, Nothing, Exception, Double] = parseLatitude or defaultLatitude
但是,如果不提供默认值,我在 or
之后采取的其他操作也可能会失败,因此情况也应该如此:
val otherAction: Validator[GeoCoords, Throwable, Nothing, Double] = ???
val finalLatitude: Validator[GeoCoords, Throwable, Exception, Double] = parseLatitude or otherAction
我已经尝试在 Validator
类型上以多种方式实现 or
,但每次它都会给我一个问题,基本上一直转换到 Any
。
def or[I2 <: I, E2 >: E, W2 >: W, B >: A](that: Validator[I2, E2, W2, B]): Validator[I2, E2, W2, B] = Validator.conversion { (i: I2) =>
val aVal: Validator[Any, E, W, A] = this.run(i)
val bVal: Validator[Any, E2, W2, 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
连接在一起并得到在末尾键入 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 Whosebug 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]
而不是 Example[A2, Any]
实际上你想要 List[A]
和 List[B]
的串联产生 List[A | B]
,其中 A | B
是 union type.
How to define "type disjunction" (union types)?
在 Scala 中没有联合类型 2 但我们可以用类型 classes 来模拟它们。
所以关于“超级简化的例子”尝试一个类型class 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, B, Out0] = LUB[A, B] { type Out = Out0 }
def instance[A, B, Out0](f: (A => Out0, B => Out0)): Aux[A, B, 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, B, A] = instance(identity, identity)
}
object LUB extends LowPriorityLUB {
implicit def aSubtypeB[A <: B, B]: Aux[A, B, B] = instance(identity, identity)
implicit def default[A, B](implicit ev: A <:!< B, ev1: B <:!< A): Aux[A, B, 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, Int]]
// implicitly[LUB.Aux[Int, AnyVal, Any]] // doesn't compile
<:!<
来自这里:
Scala: Enforcing A is not a subtype of B
如果你希望Example
是协变的
case class Example[+A, +B]...
使LUB
和LUB.Aux
逆变
trait LUB[-A, -B]...
type Aux[-A, -B, Out0] = LUB[A, B] { type Out = Out0 }