scala - 未装箱的标记类型安全吗?

标签 scala scalaz scala-2.11 scalaz7

我最近听说了 scala 中的未装箱标记类型,当我试图了解它到底是如何工作的时,我发现了这个 question这指出了 scalaz 中的实现存在的问题。修复的后果之一是必须显式解开标记类型:

def bmi(mass: Double @@ Kg, height: Double @@ M): Double =
  Tag.unwrap(mass) / pow(Tag.unwrap(height), 2)

然后我考虑了最初的想法,我可以做这样的事情:

type Tagged[U] = { type Tag = U }
type @@[T, U] = T with Tagged[U]

trait Kilogram
trait Meter
type Kg = Double @@ Kilogram
type M = Double @@ Meter

def bmi(mass: Kg, height: M): Double = mass / pow(height, 2)  

所以现在我想知道以前在 scalaz 中发现的问题是否特定于它的方法,或者简单的实现是否也可能存在删除、数组或可变参数的问题。问题是我还在学习 scala,所以我对它的类型系统的理解非常有限,我自己无法弄清楚。

最佳答案

从类型安全的角度来看,这是不安全的。 T @@ UT 的子类型,并且 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],就可以用 List[Int @@ Max] 进行替换。

关于scala - 未装箱的标记类型安全吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36505425/

相关文章:

scala - 如何在设置属性值之前和之后存储对象状态

java - 如果我从 root pom 的命令行运行插件,它在遍历模块时会做什么?

scala - scalaz.Equal 是如何解决的

scala - 在 Scalaz 中将 Free 与非仿函数一起使用

scala - 类型别名与 lambda 类型

json - 为什么这个 json4s 代码在 scala repl 中工作但无法编译?

arrays - 隐式类适用于所有 Traversable 子类,包括 Array

scala - 关于 Scala 的赋值和 setter 方法

xml - 在 Scala 中解析不带引号的 XML

Scala 通用函数