从类型安全的角度来看,它是不安全的。T @@ U
是的子类型,T
并且T @@ U
可以在任何T
需要的地方使用的实例,即使它是偶然的。考虑以下
type Tagged[U] = { type Tag = U }
type @@[T, U] = T with Tagged[U]
object Tag {
def apply[@specialized A, T](a: A): A @@ T = a.asInstanceOf[A @@ T]
}
trait Compare[A] { def compare(a1: A, a2: A): Int }
def useCompare[A: Compare](l: List[A]): Option[A] =
l.foldLeft(Option.empty[A])((xs, x) =>
xs.fold(Some(x))(xxs =>
if (implicitly[Compare[A]].compare(xxs, x) <= 0) Some(xxs)
else Some(x)))
implicit def intCompare: Compare[Int] = new Compare[Int] {
def compare(a1: Int, a2: Int): Int =
a1.compareTo(a2)
}
trait Max
implicit def intCompareMax: Compare[Int @@ Max] = new Compare[Int @@ Max] {
def compare(a1: Int @@ Max, a2: Int @@ Max): Int =
a1.compareTo(a2) * -1
}
scala> val listInts: List[Int] = List(1, 2, 3, 4)
listInts: List[Int] = List(1, 2, 3, 4)
scala> val min = useCompare(listInts)
min: Option[Int] = Some(1)
scala> val listIntMaxs: List[Int @@ Max] = listInts.map(Tag[Int, Max])
listIntMaxs: List[@@[Int,Max]] = List(1, 2, 3, 4)
scala> val max = useCompare(listIntMaxs)
max: Option[@@[Int,Max]] = Some(4)
好的,一切都很酷,对吧?这就是T @@ U
存在的原因。我们希望能够创建一个新类型并为它定义新的类型类。不幸的是,当您的同事出现并执行一些有效的重构并意外破坏您的业务逻辑时,一切都不好。
scala> val max = useCompare(listIntMaxs ::: List.empty[Int])
max: Option[Int] = Some(1)
哎呀
在这种情况下,使用子类型,结合List[+A]
类型参数的协方差导致了一个错误。List[Int @@ Max]
可以在List[Int]
需要 a 的地方替换A。