问题描述
我正在尝试查看是否有找到类型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,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
,所以也许我想给它一个默认值,但是仍然可以理解它失败了。我想将Exception
从E
错误参数移到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 | B
是union 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
如果您希望Example
是协变的
case class Example[+A,+B]...
使LUB
和LUB.Aux
互变
trait LUB[-A,-B]...
type Aux[-A,-B,B] { type Out = Out0 }