简体   繁体   English

unboxed标记类型是否安全?

[英]Are unboxed tagged types safe?

I've recently heard about unboxed tagged types in scala and while I was trying to learn how exactly it works, I've found this question that points to problems the implementation in scalaz had. 我最近听说过scala中的未装箱标记类型,当我试图了解它是如何工作的时候,我发现这个问题指出了scalaz实现的问题。 One of the consequences of the fix was having to explicit unwrap the tagged type: 修复的后果之一是必须显式解包标记类型:

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

Then I considered the original idea, where I could do something like: 然后我考虑了最初的想法,在那里我可以做以下事情:

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)  

So now I'm wondering whether the issues found previously in scalaz are specific to it's approach, or if the simple implementation could also have problems with erasure, arrays or varargs. 所以现在我想知道先前在scalaz中发现的问题是否特定于它的方法,或者简单的实现是否也可能存在擦除,数组或变量问题。 The thing is I'm still learning scala, so my understanding of it's type system is quite limited and I couldn't figure it out by myself. 问题是我还在学习scala,所以我对它的类型系统的理解非常有限,我自己无法弄明白。

It is unsafe from a type safety perspective. 从类型安全角度来看,这是不安全的。 T @@ U is a subtype of T and an instance of T @@ U may be used where ever an instance of T is required, even when it is accidental. T @@ U是的子类型T和实例T @@ U以往的一个实例,其中可以使用T是必需的,即使它是偶然的。 Consider the following 考虑以下

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)

OK, everything is cool, right? 好吧,一切都很酷,对吗? This is why T @@ U exists. 这就是T @@ U存在的原因。 We want to be able to make a new type and define new type classes for it. 我们希望能够创建一个新类型并为其定义新的类型。 Unfortunately, everything isn't ok when your coworker comes along and performs some valid refactoring and accidentally breaks your business logic. 不幸的是,当你的同事出现并执行一些有效的重构并意外破坏你的业务逻辑时,一切都不行。

scala> val max = useCompare(listIntMaxs ::: List.empty[Int])
max: Option[Int] = Some(1)

Oops 哎呀

In this case, the use of subtyping, combined with the covariance on the List[+A] type parameter caused a bug. 在这种情况下,使用子类型,结合List[+A]类型参数的协方差导致了一个bug。 A List[Int @@ Max] may be substituted wherever a List[Int] is needed. 只要需要List[Int] List[Int @@ Max]就可以替换。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM