我最近听说过scala中的未装箱标记类型,当我试图了解它是如何工作时,我发现这个question指出了scalaz中的实现存在的问题。修复的后果之一是必须显式解包标记类型:
public static void rotateRow(int[][] arr, int row) {
int newCurrent = arr[row][arr.length - 1];
int nextCurrent;
for (int currentIndex = 0; currentIndex < arr.length; currentIndex++) {
nextCurrent = arr[row][currentIndex];
arr[row][currentIndex] = newCurrent;
newCurrent = nextCurrent;
}
}
然后我考虑了最初的想法,在那里我可以做一些像:
def bmi(mass: Double @@ Kg, height: Double @@ M): Double =
Tag.unwrap(mass) / pow(Tag.unwrap(height), 2)
所以现在我想知道先前在scalaz中发现的问题是否特定于它的方法,或者简单实现是否也存在擦除,数组或变量问题。问题是我还在学习scala,所以我对它的类型系统的理解是非常有限的,我自己无法弄明白。
答案 0 :(得分:0)
从类型安全角度来看,这是不安全的。 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]
。